diff --git a/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainControls.kt b/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainControls.kt
index 833fef72..534ae664 100644
--- a/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainControls.kt
+++ b/app/src/main/kotlin/com/subtracks/ui/settings/ReplayGainControls.kt
@@ -17,6 +17,13 @@ internal val REPLAY_GAIN_PREAMP_STEPS =
// storing it.
internal fun snapReplayGainPreampDb(value: Float): Float = (value / REPLAY_GAIN_PREAMP_STEP_DB).roundToInt() * REPLAY_GAIN_PREAMP_STEP_DB
+internal fun stepReplayGainPreampDb(
+ value: Float,
+ steps: Int,
+): Float =
+ snapReplayGainPreampDb(value + steps * REPLAY_GAIN_PREAMP_STEP_DB)
+ .coerceIn(MIN_REPLAY_GAIN_PREAMP_DB, MAX_REPLAY_GAIN_PREAMP_DB)
+
internal fun replayGainModeLabel(
resources: Resources,
mode: ReplayGainMode,
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 7df3d06b..b82ab683 100644
--- a/app/src/main/kotlin/com/subtracks/ui/settings/SettingsScreen.kt
+++ b/app/src/main/kotlin/com/subtracks/ui/settings/SettingsScreen.kt
@@ -28,6 +28,7 @@ import androidx.compose.material.icons.rounded.Edit
import androidx.compose.material.icons.rounded.Favorite
import androidx.compose.material.icons.rounded.Info
import androidx.compose.material.icons.rounded.Language
+import androidx.compose.material.icons.rounded.Remove
import androidx.compose.material.icons.rounded.Share
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
@@ -779,13 +780,44 @@ private fun ReplayGainPreampDialog(
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary,
)
- Slider(
- value = draft,
- onValueChange = { draft = snapReplayGainPreampDb(it) },
- valueRange = MIN_REPLAY_GAIN_PREAMP_DB..MAX_REPLAY_GAIN_PREAMP_DB,
- steps = REPLAY_GAIN_PREAMP_STEPS,
- )
- Text(replayGainPreampLabel(resources, draft))
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ IconButton(
+ onClick = { draft = stepReplayGainPreampDb(draft, -1) },
+ enabled = draft > MIN_REPLAY_GAIN_PREAMP_DB,
+ ) {
+ Icon(
+ Icons.Rounded.Remove,
+ contentDescription = stringResource(R.string.settings_replay_gain_preamp_decrease),
+ )
+ }
+ Slider(
+ value = draft,
+ onValueChange = { draft = snapReplayGainPreampDb(it) },
+ valueRange = MIN_REPLAY_GAIN_PREAMP_DB..MAX_REPLAY_GAIN_PREAMP_DB,
+ steps = REPLAY_GAIN_PREAMP_STEPS,
+ modifier = Modifier.weight(1f),
+ )
+ IconButton(
+ onClick = { draft = stepReplayGainPreampDb(draft, 1) },
+ enabled = draft < MAX_REPLAY_GAIN_PREAMP_DB,
+ ) {
+ Icon(
+ Icons.Rounded.Add,
+ contentDescription = stringResource(R.string.settings_replay_gain_preamp_increase),
+ )
+ }
+ }
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ Text(replayGainPreampLabel(resources, draft))
+ Spacer(Modifier.weight(1f))
+ TextButton(
+ onClick = { draft = 0f },
+ enabled = draft != 0f,
+ ) { Text(stringResource(R.string.settings_replay_gain_preamp_reset)) }
+ }
}
},
confirmButton = {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b266bccf..439d8c73 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -77,7 +77,10 @@
Album gain
Off
Track gain
+ Lower preamp
+ Raise preamp
Adjust level on top of the replay gain
+ Reset
Preamp
%1$s dB
Normalise loudness using the gain from the server
diff --git a/app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainControlsTest.kt b/app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainControlsTest.kt
index b6a5032d..5b08e816 100644
--- a/app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainControlsTest.kt
+++ b/app/src/test/kotlin/com/subtracks/ui/settings/ReplayGainControlsTest.kt
@@ -34,6 +34,14 @@ class ReplayGainControlsTest {
assertEquals(59, REPLAY_GAIN_PREAMP_STEPS)
}
+ @Test
+ fun thePreampStepsAlongTheGridAndStopsAtTheEnds() {
+ assertEquals(-1.5f, stepReplayGainPreampDb(-1f, -1), 0f)
+ assertEquals(-0.5f, stepReplayGainPreampDb(-1f, 1), 0f)
+ assertEquals(MIN_REPLAY_GAIN_PREAMP_DB, stepReplayGainPreampDb(MIN_REPLAY_GAIN_PREAMP_DB, -1), 0f)
+ assertEquals(MAX_REPLAY_GAIN_PREAMP_DB, stepReplayGainPreampDb(MAX_REPLAY_GAIN_PREAMP_DB, 1), 0f)
+ }
+
@Test
fun thePreampSnapsBackOntoTheHalfDecibelGrid() {
assertEquals(0f, snapReplayGainPreampDb(-0f), 0f)