From da3b760f43266791266af7b6bbf8e4593fd41d6a Mon Sep 17 00:00:00 2001 From: Jeena Date: Tue, 10 Mar 2026 02:22:10 +0000 Subject: [PATCH] audio: Apply square curve to volume slider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AudioTrack.setVolume() takes a linear amplitude value, but human hearing is logarithmic — a linear 0-1 slider sounds like all the volume is crammed into the bottom quarter of the range. Squaring the slider value (amplitude = linear²) spreads perceived loudness evenly across the full slider travel. --- app/src/main/kotlin/net/jeena/pacer/AudioEngine.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/net/jeena/pacer/AudioEngine.kt b/app/src/main/kotlin/net/jeena/pacer/AudioEngine.kt index 7017918..58f7b9e 100644 --- a/app/src/main/kotlin/net/jeena/pacer/AudioEngine.kt +++ b/app/src/main/kotlin/net/jeena/pacer/AudioEngine.kt @@ -22,6 +22,11 @@ class AudioEngine { companion object { private const val BEEP_DURATION_MS = 40 private const val SILENCE_CHUNK_MS = 10 + + // Apply a square curve so the slider feels linear to human hearing. + // AudioTrack.setVolume() takes a linear amplitude; our 0-1 slider + // value is perceived as "all the volume at the bottom" without this. + private fun toAmplitude(linear: Float): Float = linear * linear } private val mainHandler = Handler(Looper.getMainLooper()) @@ -69,7 +74,7 @@ class AudioEngine { .setTransferMode(AudioTrack.MODE_STREAM) .build() - audioTrack?.setVolume(currentVolume) + audioTrack?.setVolume(toAmplitude(currentVolume)) audioTrack?.play() handlerThread = HandlerThread("PacerAudio").also { it.start() } @@ -94,7 +99,7 @@ class AudioEngine { fun setVolume(volume: Float) { currentVolume = BpmCalculator.clampVolume(volume) - audioTrack?.setVolume(currentVolume) + audioTrack?.setVolume(toAmplitude(currentVolume)) } private fun audioLoop() {