diff --git a/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainControls.kt b/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainControls.kt new file mode 100644 index 00000000..833fef72 --- /dev/null +++ b/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainControls.kt @@ -0,0 +1,37 @@ +package com.subtracks.ui.settings + +import android.content.res.Resources +import com.subtracks.R +import com.subtracks.data.prefs.MAX_REPLAY_GAIN_PREAMP_DB +import com.subtracks.data.prefs.MIN_REPLAY_GAIN_PREAMP_DB +import com.subtracks.data.prefs.ReplayGainMode +import kotlin.math.roundToInt + +internal const val REPLAY_GAIN_PREAMP_STEP_DB = 0.5f + +internal val REPLAY_GAIN_PREAMP_STEPS = + ((MAX_REPLAY_GAIN_PREAMP_DB - MIN_REPLAY_GAIN_PREAMP_DB) / REPLAY_GAIN_PREAMP_STEP_DB).toInt() - 1 + +// Material's slider snaps in pixel space and converts back, so the value it reports is near, but not +// on, the grid it was configured with (for example -0.99999905 for -1.0). Snap before showing or +// storing it. +internal fun snapReplayGainPreampDb(value: Float): Float = (value / REPLAY_GAIN_PREAMP_STEP_DB).roundToInt() * REPLAY_GAIN_PREAMP_STEP_DB + +internal fun replayGainModeLabel( + resources: Resources, + mode: ReplayGainMode, +): String = + when (mode) { + ReplayGainMode.Off -> resources.getString(R.string.settings_replay_gain_mode_off) + ReplayGainMode.Track -> resources.getString(R.string.settings_replay_gain_mode_track) + ReplayGainMode.Album -> resources.getString(R.string.settings_replay_gain_mode_album) + } + +internal fun replayGainPreampLabel( + resources: Resources, + decibels: Float, +): String { + val sign = if (decibels > 0f) "+" else "" + val magnitude = String.format(resources.configuration.locales[0], "%.1f", decibels) + return resources.getString(R.string.settings_replay_gain_preamp_value, "$sign$magnitude") +} diff --git a/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainLabels.kt b/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainLabels.kt deleted file mode 100644 index 4bfc533e..00000000 --- a/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainLabels.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.subtracks.ui.settings - -import android.content.res.Resources -import com.subtracks.R -import com.subtracks.data.prefs.ReplayGainMode - -internal fun replayGainModeLabel( - resources: Resources, - mode: ReplayGainMode, -): String = - when (mode) { - ReplayGainMode.Off -> resources.getString(R.string.settings_replay_gain_mode_off) - ReplayGainMode.Track -> resources.getString(R.string.settings_replay_gain_mode_track) - ReplayGainMode.Album -> resources.getString(R.string.settings_replay_gain_mode_album) - } - -internal fun replayGainPreampLabel( - resources: Resources, - decibels: Float, -): String { - val sign = if (decibels > 0f) "+" else "" - return resources.getString(R.string.settings_replay_gain_preamp_value, "$sign$decibels") -} diff --git a/app/src/main/kotlin/com/subtracks/ui/settings/SettingsScreen.kt b/app/src/main/kotlin/com/subtracks/ui/settings/SettingsScreen.kt index 3709857a..7df3d06b 100644 --- a/app/src/main/kotlin/com/subtracks/ui/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/settings/SettingsScreen.kt @@ -361,7 +361,6 @@ fun SettingsScreen( colors = ListItemDefaults.colors(containerColor = Color.Transparent), ) } - item { SectionHeader(stringResource(R.string.settings_music_name)) } item { ListItem( headlineContent = { Text(stringResource(R.string.settings_replay_gain_title)) }, @@ -729,8 +728,6 @@ private fun SyncModeDialog( ) } -private const val REPLAY_GAIN_PREAMP_STEPS = 59 - @Composable private fun ReplayGainModeDialog( selected: ReplayGainMode, @@ -771,7 +768,7 @@ private fun ReplayGainPreampDialog( onDismiss: () -> Unit, ) { val resources = LocalResources.current - var draft by remember(selected) { mutableFloatStateOf(selected) } + var draft by remember(selected) { mutableFloatStateOf(snapReplayGainPreampDb(selected)) } AlertDialog( onDismissRequest = onDismiss, title = { Text(stringResource(R.string.settings_replay_gain_preamp_title)) }, @@ -784,7 +781,7 @@ private fun ReplayGainPreampDialog( ) Slider( value = draft, - onValueChange = { draft = it }, + onValueChange = { draft = snapReplayGainPreampDb(it) }, valueRange = MIN_REPLAY_GAIN_PREAMP_DB..MAX_REPLAY_GAIN_PREAMP_DB, steps = REPLAY_GAIN_PREAMP_STEPS, ) diff --git a/app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainLabelsTest.kt b/app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainControlsTest.kt similarity index 54% rename from app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainLabelsTest.kt rename to app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainControlsTest.kt index 3d5bddbe..b6a5032d 100644 --- a/app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainLabelsTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainControlsTest.kt @@ -3,13 +3,15 @@ package com.subtracks.ui.settings import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.subtracks.data.prefs.MAX_REPLAY_GAIN_PREAMP_DB +import com.subtracks.data.prefs.MIN_REPLAY_GAIN_PREAMP_DB import com.subtracks.data.prefs.ReplayGainMode import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class ReplayGainLabelsTest { +class ReplayGainControlsTest { private val resources = ApplicationProvider.getApplicationContext().resources @Test @@ -25,4 +27,20 @@ class ReplayGainLabelsTest { assertEquals("-2.5 dB", replayGainPreampLabel(resources, -2.5f)) assertEquals("0.0 dB", replayGainPreampLabel(resources, 0f)) } + + @Test + fun theSliderHasOneStepPerHalfDecibel() { + assertEquals(60, ((MAX_REPLAY_GAIN_PREAMP_DB - MIN_REPLAY_GAIN_PREAMP_DB) / REPLAY_GAIN_PREAMP_STEP_DB).toInt()) + assertEquals(59, REPLAY_GAIN_PREAMP_STEPS) + } + + @Test + fun thePreampSnapsBackOntoTheHalfDecibelGrid() { + assertEquals(0f, snapReplayGainPreampDb(-0f), 0f) + assertEquals(-1f, snapReplayGainPreampDb(-0.99999905f), 0f) + assertEquals(-3f, snapReplayGainPreampDb(-3.000001f), 0f) + assertEquals(14.5f, snapReplayGainPreampDb(14.6f), 0f) + assertEquals(MAX_REPLAY_GAIN_PREAMP_DB, snapReplayGainPreampDb(14.9f), 0f) + assertEquals(MIN_REPLAY_GAIN_PREAMP_DB, snapReplayGainPreampDb(-14.9f), 0f) + } }