Tap tempo by tapping the BPM readout (v0.15.0)
Tapping the "### BPM" label sets the tempo from the average interval of recent taps (up to 8, refining as you go); a pause over 2 s starts a fresh count. The −/+ nudge buttons are unchanged. Purely UI-side — a small remembered list of tap timestamps, no model/engine changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -22,8 +22,8 @@ android {
|
||||
// (android.media.midi) and AAudio low-latency audio we rely on.
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 38
|
||||
versionName = "0.14.0"
|
||||
versionCode = 39
|
||||
versionName = "0.15.0"
|
||||
|
||||
// We provide our own instrumentation runner if/when tests are added.
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package space.rcmd.android.sizzle.ui.tracker
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -19,8 +20,10 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -147,14 +150,31 @@ private fun TransportButtons(vm: AppViewModel) {
|
||||
// Fixed width so the button doesn't resize as the label toggles.
|
||||
RetroButton(if (isPlaying) "❚❚ PAUSE" else "▶ PLAY",
|
||||
active = isPlaying, width = 112.dp, onClick = vm::playPause)
|
||||
// Tempo nudger: tap -/+ around a fixed-width readout.
|
||||
// Tempo nudger: tap -/+ around a fixed-width readout. Tapping the readout itself
|
||||
// is TAP TEMPO — two or more taps in time set the tempo from the average interval.
|
||||
val tapTimes = remember { ArrayList<Long>() }
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
RepeatButton("-", onStep = { vm.setTempo(project.tempoBpm - 1) })
|
||||
Text(
|
||||
"${project.tempoBpm.toInt()} BPM",
|
||||
color = c.text, fontFamily = FontFamily.Monospace, fontSize = 12.sp,
|
||||
textAlign = TextAlign.Center, maxLines = 1,
|
||||
modifier = Modifier.width(72.dp).padding(vertical = 6.dp),
|
||||
modifier = Modifier
|
||||
.width(72.dp)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures {
|
||||
val now = System.currentTimeMillis()
|
||||
// A long pause starts a fresh count rather than averaging across it.
|
||||
if (tapTimes.isNotEmpty() && now - tapTimes.last() > TAP_RESET_MS) tapTimes.clear()
|
||||
tapTimes.add(now)
|
||||
while (tapTimes.size > TAP_MAX) tapTimes.removeAt(0)
|
||||
if (tapTimes.size >= 2) {
|
||||
val span = tapTimes.last() - tapTimes.first()
|
||||
if (span > 0) vm.setTempo(60_000f * (tapTimes.size - 1) / span)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(vertical = 6.dp),
|
||||
)
|
||||
RepeatButton("+", onStep = { vm.setTempo(project.tempoBpm + 1) })
|
||||
}
|
||||
@@ -224,3 +244,8 @@ private fun EditButtons(vm: AppViewModel) {
|
||||
// Swap the bottom half between the arranger and the punch-in keyboard.
|
||||
RetroButton("⌨ KBD", active = vm.punchInMode, onClick = vm::togglePunchIn)
|
||||
}
|
||||
|
||||
// Tap-tempo tuning: a gap longer than this starts a fresh count; keep at most this
|
||||
// many recent taps in the averaging window.
|
||||
private const val TAP_RESET_MS = 2000L
|
||||
private const val TAP_MAX = 8
|
||||
|
||||
Reference in New Issue
Block a user