diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt index d2aa4745e..23c7fd490 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeViewModel.kt @@ -145,7 +145,7 @@ class HomeViewModel : ViewModel(), SettingsManager.Callback, MusicStore.Callback } } - override fun onLibTabsUpdate(libTabs: Array) { + override fun onLibraryChanged() { tabs = visibleTabs _shouldRecreateTabs.value = true } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainAudioProcessor.kt b/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainAudioProcessor.kt similarity index 89% rename from app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainAudioProcessor.kt rename to app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainAudioProcessor.kt index aaaa34b2c..8339530ab 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainAudioProcessor.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainAudioProcessor.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.oxycblt.auxio.playback.system +package org.oxycblt.auxio.playback.replaygain import com.google.android.exoplayer2.C import com.google.android.exoplayer2.audio.AudioProcessor @@ -23,6 +23,7 @@ import com.google.android.exoplayer2.audio.BaseAudioProcessor import com.google.android.exoplayer2.metadata.Metadata import com.google.android.exoplayer2.metadata.id3.TextInformationFrame import com.google.android.exoplayer2.metadata.vorbis.VorbisComment +import java.lang.UnsupportedOperationException import java.nio.ByteBuffer import kotlin.math.pow import org.oxycblt.auxio.music.Album @@ -39,8 +40,6 @@ import org.oxycblt.auxio.util.unlikelyToBeNull * manipulates the bitstream itself to modify the volume, which allows the use of positive * ReplayGain values. * - * TODO: Pre-amp values - * * @author OxygenCobalt */ class ReplayGainAudioProcessor : BaseAudioProcessor() { @@ -64,23 +63,21 @@ class ReplayGainAudioProcessor : BaseAudioProcessor() { * Vanilla Music's implementation. */ fun applyReplayGain(metadata: Metadata?) { - if (metadata == null || settingsManager.replayGainMode == ReplayGainMode.OFF) { - logW( - "Not applying replaygain [" + - "metadata: ${metadata != null}, " + - "enabled: ${settingsManager.replayGainMode == ReplayGainMode.OFF}]") + if (settingsManager.replayGainMode == ReplayGainMode.OFF) { + logW("ReplayGain not enabled") volume = 1f return } - val gain = parseReplayGain(metadata) + val gain = metadata?.let(::parseReplayGain) + val preAmp = settingsManager.replayGainPreAmp val adjust = if (gain != null) { // ReplayGain is configurable, so determine what to do based off of the mode. val useAlbumGain = when (settingsManager.replayGainMode) { - ReplayGainMode.OFF -> error("Unreachable") + ReplayGainMode.OFF -> throw UnsupportedOperationException() // User wants track gain to be preferred. Default to album gain only if // there is no track gain. @@ -96,18 +93,25 @@ class ReplayGainAudioProcessor : BaseAudioProcessor() { playbackManager.song?.album?.id == playbackManager.parent?.id } - if (useAlbumGain) { - logD("Using album gain") - gain.album - } else { - logD("Using track gain") - gain.track - } + val resolvedGain = + if (useAlbumGain) { + logD("Using album gain") + gain.album + } else { + logD("Using track gain") + gain.track + } + + // Apply the "With tags" adjustment + resolvedGain + preAmp.with } else { - // No gain tags were present - 0f + // No gain tags were present, just apply the adjustment without tags. + logD("No ReplayGain tags present ") + preAmp.without } + logD("Applying ReplayGain adjustment ${adjust}db") + // Final adjustment along the volume curve. volume = 10f.pow(adjust / 20f) } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt b/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainModels.kt similarity index 90% rename from app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt rename to app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainModels.kt index d9269de48..387d7cea4 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/ReplayGainMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/replaygain/ReplayGainModels.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.oxycblt.auxio.playback.system +package org.oxycblt.auxio.playback.replaygain import org.oxycblt.auxio.IntegerTable @@ -42,3 +42,9 @@ enum class ReplayGainMode { } } } + +/** Represents the ReplayGain pre-amp */ +data class ReplayGainPreAmp( + val with: Float, + val without: Float, +) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/MediaButtonReceiver.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/MediaButtonReceiver.kt index 6525bcdbc..a2fe9bf1c 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/MediaButtonReceiver.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/MediaButtonReceiver.kt @@ -29,7 +29,6 @@ import android.content.Intent * KitKat don't break! To prevent Auxio from not showing up at all in these apps, we declare a * BroadcastReceiver in the manifest that actually does nothing. Any broadcast by apps should be * routed by the media session when the service exists. - * @author OxygenCobalt */ class MediaButtonReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) {} diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/MediaSessionComponent.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/MediaSessionComponent.kt index a3a6098d6..fbf983a6d 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/MediaSessionComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/MediaSessionComponent.kt @@ -52,6 +52,7 @@ class MediaSessionComponent(private val context: Context, private val player: Pl init { player.addListener(this) playbackManager.addCallback(this) + settingsManager.addCallback(this) mediaSession.setCallback(this) } @@ -101,9 +102,6 @@ class MediaSessionComponent(private val context: Context, private val player: Pl .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, song.track?.toLong() ?: 0L) .putText(MediaMetadataCompat.METADATA_KEY_DATE, song.album.year?.toString()) .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.durationMs) - .putText( - MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, - song.album.albumCoverUri.toString()) // Normally, android expects one to provide a URI to the metadata instance instead of // a full blown bitmap. In practice, this is not ideal in the slightest, as we cannot @@ -145,11 +143,7 @@ class MediaSessionComponent(private val context: Context, private val player: Pl // --- SETTINGSMANAGER CALLBACKS --- - override fun onShowCoverUpdate(showCovers: Boolean) { - updateMediaMetadata(playbackManager.song) - } - - override fun onQualityCoverUpdate(doQualityCovers: Boolean) { + override fun onCoverSettingsChanged() { updateMediaMetadata(playbackManager.song) } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/NotificationComponent.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/NotificationComponent.kt index 15b9d16a4..568f0e472 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/NotificationComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/NotificationComponent.kt @@ -35,13 +35,14 @@ import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.util.getSystemServiceSafe +import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.newBroadcastIntent import org.oxycblt.auxio.util.newMainIntent /** * The unified notification for [PlaybackService]. Due to the nature of how this notification is * used, it is *not self-sufficient*. Updates have to be delivered manually, as to prevent state - * inconsistency when the foreground state is started. + * inconsistency derived from callback order. * @author OxygenCobalt */ @SuppressLint("RestrictedApi") @@ -108,6 +109,7 @@ class NotificationComponent( object : BitmapProvider.Target { override fun onCompleted(bitmap: Bitmap?) { setLargeIcon(bitmap) + setLargeIcon(null) callback.onNotificationChanged(this@NotificationComponent) } }) 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 5e5eaee19..5002c4dfa 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 @@ -47,6 +47,7 @@ import org.oxycblt.auxio.BuildConfig import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.Song +import org.oxycblt.auxio.playback.replaygain.ReplayGainAudioProcessor import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.settings.SettingsManager @@ -291,24 +292,18 @@ class PlaybackService : // --- SETTINGSMANAGER OVERRIDES --- - override fun onReplayGainUpdate(mode: ReplayGainMode) { + override fun onReplayGainSettingsChanged() { onTracksInfoChanged(player.currentTracksInfo) } - override fun onShowCoverUpdate(showCovers: Boolean) { + override fun onCoverSettingsChanged() { playbackManager.song?.let { song -> notificationComponent.updateMetadata(song, playbackManager.parent) } } - override fun onQualityCoverUpdate(doQualityCovers: Boolean) { - playbackManager.song?.let { song -> - notificationComponent.updateMetadata(song, playbackManager.parent) - } - } - - override fun onNotifActionUpdate(useAltAction: Boolean) { - if (useAltAction) { + override fun onNotifSettingsChanged() { + if (settingsManager.useAltNotifAction) { onShuffledChanged(playbackManager.isShuffled) } else { onRepeatChanged(playbackManager.repeatMode) diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt index 3dc82fe04..fb552911f 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt @@ -61,7 +61,7 @@ class SettingsListFragment : PreferenceFragmentCompat() { preferenceManager.onDisplayPreferenceDialogListener = this preferenceScreen.children.forEach(::recursivelyHandlePreference) - // Make the RecycleBiew edge-to-edge capable + // Make the RecycleView edge-to-edge capable view.findViewById(androidx.preference.R.id.recycler_view).apply { clipToPadding = false 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 99815520d..b785017cf 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsManager.kt @@ -24,7 +24,8 @@ import androidx.core.content.edit import androidx.preference.PreferenceManager 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.playback.replaygain.ReplayGainMode +import org.oxycblt.auxio.playback.replaygain.ReplayGainPreAmp import org.oxycblt.auxio.ui.DisplayMode import org.oxycblt.auxio.ui.Sort import org.oxycblt.auxio.ui.accent.Accent @@ -104,6 +105,20 @@ class SettingsManager private constructor(context: Context) : ReplayGainMode.fromIntCode(prefs.getInt(KEY_REPLAY_GAIN, Int.MIN_VALUE)) ?: ReplayGainMode.OFF + /** The current ReplayGain pre-amp configuration */ + var replayGainPreAmp: ReplayGainPreAmp + get() = + ReplayGainPreAmp( + prefs.getFloat(KEY_REPLAY_GAIN_PRE_AMP_WITH, 0f), + prefs.getFloat(KEY_REPLAY_GAIN_PRE_AMP_WITHOUT, 0f)) + set(value) { + prefs.edit { + putFloat(KEY_REPLAY_GAIN_PRE_AMP_WITH, value.with) + putFloat(KEY_REPLAY_GAIN_PRE_AMP_WITHOUT, value.without) + apply() + } + } + /** What queue to create when a song is selected (ex. From All Songs or Search) */ val songPlaybackMode: PlaybackMode get() = @@ -186,6 +201,7 @@ class SettingsManager private constructor(context: Context) : Sort.fromIntCode(prefs.getInt(KEY_DETAIL_ALBUM_SORT, Int.MIN_VALUE)) ?: Sort.ByDisc(true) + // Correct legacy album sort modes to Disc if (sort is Sort.ByName) { sort = Sort.ByDisc(sort.isAscending) } @@ -239,12 +255,11 @@ class SettingsManager private constructor(context: Context) : override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { when (key) { - KEY_USE_ALT_NOTIFICATION_ACTION -> - callbacks.forEach { it.onNotifActionUpdate(useAltNotifAction) } - KEY_SHOW_COVERS -> callbacks.forEach { it.onShowCoverUpdate(showCovers) } - KEY_QUALITY_COVERS -> callbacks.forEach { it.onQualityCoverUpdate(useQualityCovers) } - KEY_LIB_TABS -> callbacks.forEach { it.onLibTabsUpdate(libTabs) } - KEY_REPLAY_GAIN -> callbacks.forEach { it.onReplayGainUpdate(replayGainMode) } + KEY_USE_ALT_NOTIFICATION_ACTION -> callbacks.forEach { it.onNotifSettingsChanged() } + KEY_SHOW_COVERS, KEY_QUALITY_COVERS -> callbacks.forEach { it.onCoverSettingsChanged() } + KEY_LIB_TABS -> callbacks.forEach { it.onLibraryChanged() } + KEY_REPLAY_GAIN, KEY_REPLAY_GAIN_PRE_AMP_WITH, KEY_REPLAY_GAIN_PRE_AMP_WITHOUT -> + callbacks.forEach { it.onReplayGainSettingsChanged() } } } @@ -254,11 +269,10 @@ class SettingsManager private constructor(context: Context) : * context. */ interface Callback { - fun onLibTabsUpdate(libTabs: Array) {} - fun onNotifActionUpdate(useAltAction: Boolean) {} - fun onShowCoverUpdate(showCovers: Boolean) {} - fun onQualityCoverUpdate(doQualityCovers: Boolean) {} - fun onReplayGainUpdate(mode: ReplayGainMode) {} + fun onLibraryChanged() {} + fun onNotifSettingsChanged() {} + fun onCoverSettingsChanged() {} + fun onReplayGainSettingsChanged() {} } companion object { @@ -276,6 +290,9 @@ class SettingsManager private constructor(context: Context) : const val KEY_HEADSET_AUTOPLAY = "auxio_headset_autoplay" const val KEY_REPLAY_GAIN = "auxio_replay_gain" + const val KEY_REPLAY_GAIN_PRE_AMP = "auxio_pre_amp" + const val KEY_REPLAY_GAIN_PRE_AMP_WITH = "auxio_pre_amp_with" + const val KEY_REPLAY_GAIN_PRE_AMP_WITHOUT = "auxio_pre_amp_without" 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/StyledImageButton.kt b/app/src/main/java/org/oxycblt/auxio/ui/StyledImageButton.kt index 21b43815c..7d187c8e8 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/StyledImageButton.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/StyledImageButton.kt @@ -35,9 +35,9 @@ import org.oxycblt.auxio.util.getDrawableSafe * More specifically, this class add two features: * - Specification of the icon size. This is to accommodate the playback buttons, which tend to be * larger as by default the playback icons look terrible with the gobs of whitespace everywhere. - * - Addition of an indicator, which is a dot that can denote when a button is active. This is - * also useful for the playback buttons, as at times highlighting them is not enough to - * differentiate them. + * - Addition of an indicator, which is a dot that can denote when a button is active. This is also + * useful for the playback buttons, as at times highlighting them is not enough to differentiate + * them. * @author OxygenCobalt */ class StyledImageButton diff --git a/app/src/main/java/org/oxycblt/auxio/ui/StyledImageView.kt b/app/src/main/java/org/oxycblt/auxio/ui/StyledImageView.kt index e9b69c005..9ce82987a 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/StyledImageView.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/StyledImageView.kt @@ -45,8 +45,8 @@ import org.oxycblt.auxio.util.getDrawableSafe * An [AppCompatImageView] that applies many of the stylistic choices that Auxio uses regarding * images. * - * Default behavior includes the addition of a tonal background, automatic sizing of icons to - * half of the view size, and corner radius application depending on user preference. + * Default behavior includes the addition of a tonal background, automatic sizing of icons to half + * of the view size, and corner radius application depending on user preference. * @author OxygenCobalt */ class StyledImageView diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt index 81be297fa..8641c581d 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -131,8 +131,7 @@ class WidgetComponent(private val context: Context) : override fun onPlayingChanged(isPlaying: Boolean) = update() override fun onShuffledChanged(isShuffled: Boolean) = update() override fun onRepeatChanged(repeatMode: RepeatMode) = update() - override fun onShowCoverUpdate(showCovers: Boolean) = update() - override fun onQualityCoverUpdate(doQualityCovers: Boolean) = update() + override fun onCoverSettingsChanged() = update() /* * An immutable condensed variant of the current playback state, used so that PlaybackStateManager