Fix! A-B looping ux
This commit is contained in:
parent
bcd2d9a1f9
commit
540b508250
4 changed files with 86 additions and 37 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package mg.dot.feufaro.solfa
|
||||
|
||||
import SharedScreenModel
|
||||
import SharedScreenModel.ABModeState
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.*
|
||||
|
|
@ -286,19 +287,21 @@ fun TimeUnitComposable(
|
|||
parentFocusRequester: FocusRequester? = null,
|
||||
transpositionInterval: Int,
|
||||
hairpinRange: HairpinRange? = null,
|
||||
isInABrange: Boolean = false
|
||||
isInABrange: Boolean = false,
|
||||
abModeState: ABModeState,
|
||||
) {
|
||||
val col = if (tuo.getNum() % 2 == 0) MaterialTheme.colorScheme.tertiary.copy(alpha = 0.05f) else MaterialTheme.colorScheme.error.copy(alpha = 0.05f)
|
||||
val currentDensity = LocalDensity.current
|
||||
val colorUnderline = MaterialTheme.colorScheme.onSecondary
|
||||
val animatedColor = when {
|
||||
gridActive -> animateColorAsState(
|
||||
targetValue = MaterialTheme.colorScheme.secondary.copy(alpha = 1f),
|
||||
animationSpec = tween(durationMillis = 100)
|
||||
).value
|
||||
val targetColor = when {
|
||||
gridActive && abModeState != ABModeState.WAITING_FOR_A -> MaterialTheme.colorScheme.secondary
|
||||
isInABrange -> MaterialTheme.colorScheme.tertiary.copy(alpha = 0.25f)
|
||||
else -> col
|
||||
}
|
||||
val animatedColor by animateColorAsState(
|
||||
targetValue = targetColor,
|
||||
animationSpec = tween(durationMillis = 100)
|
||||
)
|
||||
|
||||
val focusRequesters = remember { List(4) { FocusRequester() } }
|
||||
|
||||
|
|
@ -694,6 +697,7 @@ fun LazyVerticalGridTUO(
|
|||
val tuoTimestamps by sharedScreenModel.tuoTimestamps.collectAsState()
|
||||
val activeRowIndex by sharedScreenModel.activeIndex.collectAsStateWithLifecycle()
|
||||
|
||||
val abModeState by sharedScreenModel.abModeState.collectAsState()
|
||||
val measures = remember(tuoList, gridColumnCount) {
|
||||
tuoList.drop(1).chunked(gridColumnCount)
|
||||
}
|
||||
|
|
@ -1107,10 +1111,12 @@ fun LazyVerticalGridTUO(
|
|||
selectedIndex = globalIndex
|
||||
showEditDialog = true
|
||||
} else {
|
||||
val isAtStart = loopState.first == -1L
|
||||
val isWaitingForB = loopState.first != -1L && !loopState.third
|
||||
|
||||
if (isWaitingForB) {
|
||||
when (abModeState) {
|
||||
ABModeState.WAITING_FOR_A -> {
|
||||
sharedScreenModel.mediaPlayer?.setPointA(globalIndex)
|
||||
sharedScreenModel.setABModeState(ABModeState.WAITING_FOR_B)
|
||||
}
|
||||
ABModeState.WAITING_FOR_B -> {
|
||||
val enableSmartB = prefs.getBoolean("smartBpoint", true)
|
||||
val smartIndex = if (enableSmartB) {
|
||||
sharedScreenModel.calculateSmartPointB(globalIndex, isZeroIndexed = true)
|
||||
|
|
@ -1118,11 +1124,15 @@ fun LazyVerticalGridTUO(
|
|||
globalIndex
|
||||
}
|
||||
sharedScreenModel.mediaPlayer?.setPointB(smartIndex)
|
||||
} else {
|
||||
|
||||
sharedScreenModel.setABModeState(ABModeState.LOOP_ACTIVE)
|
||||
}
|
||||
ABModeState.IDLE, ABModeState.LOOP_ACTIVE -> {
|
||||
sharedScreenModel.seekToGrid(globalIndex)
|
||||
sharedScreenModel.updateActiveIndexByIndex(globalIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onDoubleTap = { offset ->
|
||||
if(editMode) {
|
||||
|
|
@ -1146,7 +1156,8 @@ fun LazyVerticalGridTUO(
|
|||
parentFocusRequester = focusRequester,
|
||||
transpositionInterval = currentInterval,
|
||||
hairpinRange = hairpinByEndBlock[oneTUO.numBlock],
|
||||
isInABrange = isInABrange
|
||||
isInABrange = isInABrange,
|
||||
abModeState = abModeState
|
||||
)
|
||||
|
||||
if (showContextualMenu && selectedIndex == globalIndex) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mg.dot.feufaro.ui
|
||||
|
||||
import SharedScreenModel
|
||||
import SharedScreenModel.ABModeState
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
|
|
@ -361,19 +362,20 @@ fun MidiControlPanel(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column {
|
||||
val abModeState by sharedScreenModel.abModeState.collectAsState()
|
||||
val isAtStart = loopState.first == -1L
|
||||
val isWaitingForB = loopState.first != -1L && !loopState.third
|
||||
val isLooping = loopState.third
|
||||
val isWaitingForB = abModeState == ABModeState.WAITING_FOR_B
|
||||
val isLooping = abModeState == ABModeState.LOOP_ACTIVE
|
||||
|
||||
|
||||
val abButtonText = when (abModeState) {
|
||||
ABModeState.WAITING_FOR_A -> "A ?"
|
||||
ABModeState.WAITING_FOR_B -> "B ?"
|
||||
ABModeState.IDLE, ABModeState.LOOP_ACTIVE -> "A B"
|
||||
}
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
when {
|
||||
isAtStart -> mediaPlayer.setPointA(sharedScreenModel.activeIndex.value)
|
||||
isWaitingForB -> mediaPlayer.setPointB(sharedScreenModel.activeIndex.value)
|
||||
isLooping -> {
|
||||
mediaPlayer.clearLoop()
|
||||
}
|
||||
}
|
||||
sharedScreenModel.toggleABSelectionMode()
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = when {
|
||||
|
|
@ -391,7 +393,7 @@ fun MidiControlPanel(
|
|||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "A",
|
||||
text = abButtonText,
|
||||
color = if (isWaitingForB || isLooping) Color.White else Color.Black,
|
||||
fontSize = MaterialTheme.typography.titleLarge.fontSize
|
||||
)
|
||||
|
|
@ -402,12 +404,6 @@ fun MidiControlPanel(
|
|||
tint = if (isLooping) Color.White else Color.Black,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "B",
|
||||
color = if (isLooping) Color.White else Color.Black,
|
||||
fontSize = MaterialTheme.typography.titleLarge.fontSize
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -451,9 +451,37 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
|||
val loopingState: StateFlow<LoopRange> = _loopingState.asStateFlow()
|
||||
|
||||
fun updateLoopingState(a: Int, b: Int) {
|
||||
updateActiveIndexByIndex(a)
|
||||
_loopingState.value = LoopRange(a, b)
|
||||
}
|
||||
|
||||
enum class ABModeState {
|
||||
IDLE,
|
||||
WAITING_FOR_A,
|
||||
WAITING_FOR_B,
|
||||
LOOP_ACTIVE
|
||||
}
|
||||
|
||||
private val _abModeState = MutableStateFlow(ABModeState.IDLE)
|
||||
val abModeState: StateFlow<ABModeState> = _abModeState.asStateFlow()
|
||||
|
||||
fun toggleABSelectionMode() {
|
||||
if(_isPlay.value) {
|
||||
togglePlayPause()
|
||||
}
|
||||
_abModeState.value = when (_abModeState.value) {
|
||||
ABModeState.IDLE -> ABModeState.WAITING_FOR_A
|
||||
ABModeState.WAITING_FOR_A, ABModeState.WAITING_FOR_B -> ABModeState.IDLE
|
||||
ABModeState.LOOP_ACTIVE -> {
|
||||
mediaPlayer?.clearLoop()
|
||||
ABModeState.IDLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setABModeState(state: ABModeState) {
|
||||
_abModeState.value = state
|
||||
}
|
||||
fun calculateSmartPointB(rawPointB: Int, isZeroIndexed: Boolean = false): Int {
|
||||
val currentMeasure = _measure.value
|
||||
val numerator = currentMeasure.substringBefore("/").toIntOrNull() ?: 4
|
||||
|
|
|
|||
|
|
@ -93,6 +93,15 @@ actual class FMediaPlayer actual constructor(
|
|||
loadSavedInstrumentsToPlayer(this)
|
||||
sequencer?.addMetaEventListener { meta ->
|
||||
if(meta.type == 47){
|
||||
if (isLoopingAB) {
|
||||
val resolution = sequencer?.sequence?.resolution?.toDouble() ?: 60.0
|
||||
val gridA = (pointA / resolution).toInt()
|
||||
seekToGrid(gridA)
|
||||
if (sequencer?.isRunning == false) {
|
||||
sequencer?.start()
|
||||
}
|
||||
return@addMetaEventListener
|
||||
}
|
||||
val totalGrids = (sequencer!!.sequence.tickLength / sequencer!!.sequence.resolution).toInt()
|
||||
val currentGrid = (sequencer!!.tickPosition / sequencer!!.sequence.resolution).toInt()
|
||||
val pendingDc = getPendingDcStep()
|
||||
|
|
@ -979,7 +988,12 @@ actual class FMediaPlayer actual constructor(
|
|||
while(isLoopingAB) {
|
||||
val currentTick = sequencer?.tickPosition?: 0L
|
||||
if (currentTick >= pointB) {
|
||||
sequencer?.tickPosition = pointA
|
||||
val resolution = sequencer?.sequence?.resolution?.toDouble() ?: 60.0
|
||||
val gridA = (pointA / resolution).toInt()
|
||||
seekToGrid(gridA)
|
||||
if (sequencer?.isRunning == false) {
|
||||
sequencer?.start()
|
||||
}
|
||||
}
|
||||
delay(50)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue