Implement smart B placementfor A-B looping based measure - windows
This commit is contained in:
parent
a628984986
commit
60c11b7ac8
6 changed files with 205 additions and 59 deletions
|
|
@ -17,8 +17,8 @@ expect class FMediaPlayer(filename: String, sharedScreenModel: SharedScreenModel
|
||||||
fun seekTo(position: Long)
|
fun seekTo(position: Long)
|
||||||
fun seekToGrid(gridIndex: Int)
|
fun seekToGrid(gridIndex: Int)
|
||||||
fun setVolume(level: Float)
|
fun setVolume(level: Float)
|
||||||
fun setPointA()
|
fun setPointA(grid: Int)
|
||||||
fun setPointB()
|
fun setPointB(grid: Int)
|
||||||
fun clearLoop()
|
fun clearLoop()
|
||||||
fun setTempo(bpm: Float)
|
fun setTempo(bpm: Float)
|
||||||
fun getCurrentBPM(): Float
|
fun getCurrentBPM(): Float
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import androidx.compose.ui.window.Popup
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
|
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
|
||||||
import cafe.adriel.voyager.navigator.internal.BackHandler
|
import cafe.adriel.voyager.navigator.internal.BackHandler
|
||||||
|
import com.russhwolf.settings.Settings
|
||||||
import feufaro.composeapp.generated.resources.Emmentaler
|
import feufaro.composeapp.generated.resources.Emmentaler
|
||||||
import feufaro.composeapp.generated.resources.PTSerif_Bold
|
import feufaro.composeapp.generated.resources.PTSerif_Bold
|
||||||
import feufaro.composeapp.generated.resources.PT_Serif_Bold_Italic
|
import feufaro.composeapp.generated.resources.PT_Serif_Bold_Italic
|
||||||
|
|
@ -59,6 +60,7 @@ import mg.dot.feufaro.TUODetailDialog
|
||||||
import mg.dot.feufaro.data.GridTUOData
|
import mg.dot.feufaro.data.GridTUOData
|
||||||
import mg.dot.feufaro.getPathValidator
|
import mg.dot.feufaro.getPathValidator
|
||||||
import mg.dot.feufaro.getPlatform
|
import mg.dot.feufaro.getPlatform
|
||||||
|
import mg.dot.feufaro.provideSettings
|
||||||
import mg.dot.feufaro.ui.rememberFileSaveLauncher
|
import mg.dot.feufaro.ui.rememberFileSaveLauncher
|
||||||
import mg.dot.feufaro.viewmodel.MidiMarkers
|
import mg.dot.feufaro.viewmodel.MidiMarkers
|
||||||
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
||||||
|
|
@ -283,18 +285,19 @@ fun TimeUnitComposable(
|
||||||
isCurrentBlock: Boolean = false,
|
isCurrentBlock: Boolean = false,
|
||||||
parentFocusRequester: FocusRequester? = null,
|
parentFocusRequester: FocusRequester? = null,
|
||||||
transpositionInterval: Int,
|
transpositionInterval: Int,
|
||||||
hairpinRange: HairpinRange? = null
|
hairpinRange: HairpinRange? = null,
|
||||||
|
isInABrange: Boolean = false
|
||||||
) {
|
) {
|
||||||
val col = if (tuo.getNum() % 2 == 0) MaterialTheme.colorScheme.tertiary.copy(alpha = 0.05f) else MaterialTheme.colorScheme.error.copy(alpha = 0.05f)
|
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 currentDensity = LocalDensity.current
|
||||||
val colorUnderline = MaterialTheme.colorScheme.onSecondary
|
val colorUnderline = MaterialTheme.colorScheme.onSecondary
|
||||||
val animatedColor = if (gridActive) {
|
val animatedColor = when {
|
||||||
animateColorAsState(
|
gridActive -> animateColorAsState(
|
||||||
targetValue = MaterialTheme.colorScheme.secondary.copy(alpha = 1f),
|
targetValue = MaterialTheme.colorScheme.secondary.copy(alpha = 1f),
|
||||||
animationSpec = tween(durationMillis = 100)
|
animationSpec = tween(durationMillis = 100)
|
||||||
).value
|
).value
|
||||||
} else {
|
isInABrange -> MaterialTheme.colorScheme.tertiary.copy(alpha = 0.25f)
|
||||||
col
|
else -> col
|
||||||
}
|
}
|
||||||
|
|
||||||
val focusRequesters = remember { List(4) { FocusRequester() } }
|
val focusRequesters = remember { List(4) { FocusRequester() } }
|
||||||
|
|
@ -636,6 +639,15 @@ fun LazyVerticalGridTUO(
|
||||||
|
|
||||||
val platform = getPlatform()
|
val platform = getPlatform()
|
||||||
val isAndroid = platform.name.startsWith("Android")
|
val isAndroid = platform.name.startsWith("Android")
|
||||||
|
val loopingState by sharedScreenModel.loopingState.collectAsState()
|
||||||
|
val activeIndex by sharedScreenModel.activeIndex.collectAsState()
|
||||||
|
val loopState by produceState(Triple(-1L, -1L, false), sharedScreenModel.mediaPlayer) {
|
||||||
|
while (true) {
|
||||||
|
value = sharedScreenModel.mediaPlayer?.getLoopState() ?: Triple(-1L, -1L, false)
|
||||||
|
kotlinx.coroutines.delay(100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val prefs: Settings = provideSettings()
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
@ -999,6 +1011,17 @@ fun LazyVerticalGridTUO(
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
measureTUOs.forEachIndexed { indexInMeasure, oneTUO ->
|
measureTUOs.forEachIndexed { indexInMeasure, oneTUO ->
|
||||||
val globalIndex = (measureIndex * gridColumnCount) + indexInMeasure
|
val globalIndex = (measureIndex * gridColumnCount) + indexInMeasure
|
||||||
|
val pointA = loopingState.first
|
||||||
|
val pointB = loopingState.second
|
||||||
|
|
||||||
|
val isInABrange = if (pointA != -1 && pointB != -1) {
|
||||||
|
val min = minOf(pointA, pointB)
|
||||||
|
val max = maxOf(pointA, pointB)
|
||||||
|
globalIndex in min..max
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
val isActive = (globalIndex == activeRowIndex)
|
val isActive = (globalIndex == activeRowIndex)
|
||||||
val isSelectedByKeyboard = (globalIndex == selectedGridIndex)
|
val isSelectedByKeyboard = (globalIndex == selectedGridIndex)
|
||||||
|
|
||||||
|
|
@ -1083,8 +1106,22 @@ fun LazyVerticalGridTUO(
|
||||||
selectedTUO = oneTUO
|
selectedTUO = oneTUO
|
||||||
selectedIndex = globalIndex
|
selectedIndex = globalIndex
|
||||||
showEditDialog = true
|
showEditDialog = true
|
||||||
|
} else {
|
||||||
|
val isAtStart = loopState.first == -1L
|
||||||
|
val isWaitingForB = loopState.first != -1L && !loopState.third
|
||||||
|
|
||||||
|
if (isWaitingForB) {
|
||||||
|
val enableSmartB = prefs.getBoolean("smartBpoint", true)
|
||||||
|
val smartIndex = if (enableSmartB) {
|
||||||
|
sharedScreenModel.calculateSmartPointB(globalIndex, isZeroIndexed = true)
|
||||||
|
} else {
|
||||||
|
globalIndex
|
||||||
|
}
|
||||||
|
sharedScreenModel.mediaPlayer?.setPointB(smartIndex)
|
||||||
} else {
|
} else {
|
||||||
sharedScreenModel.seekToGrid(globalIndex)
|
sharedScreenModel.seekToGrid(globalIndex)
|
||||||
|
sharedScreenModel.updateActiveIndexByIndex(globalIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDoubleTap = { offset ->
|
onDoubleTap = { offset ->
|
||||||
|
|
@ -1108,7 +1145,8 @@ fun LazyVerticalGridTUO(
|
||||||
isCurrentBlock = isSelectedByKeyboard && editMode,
|
isCurrentBlock = isSelectedByKeyboard && editMode,
|
||||||
parentFocusRequester = focusRequester,
|
parentFocusRequester = focusRequester,
|
||||||
transpositionInterval = currentInterval,
|
transpositionInterval = currentInterval,
|
||||||
hairpinRange = hairpinByEndBlock[oneTUO.numBlock]
|
hairpinRange = hairpinByEndBlock[oneTUO.numBlock],
|
||||||
|
isInABrange = isInABrange
|
||||||
)
|
)
|
||||||
|
|
||||||
if (showContextualMenu && selectedIndex == globalIndex) {
|
if (showContextualMenu && selectedIndex == globalIndex) {
|
||||||
|
|
|
||||||
|
|
@ -368,8 +368,8 @@ fun MidiControlPanel(
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
when {
|
when {
|
||||||
isAtStart -> mediaPlayer.setPointA()
|
isAtStart -> mediaPlayer.setPointA(sharedScreenModel.activeIndex.value)
|
||||||
isWaitingForB -> mediaPlayer.setPointB()
|
isWaitingForB -> mediaPlayer.setPointB(sharedScreenModel.activeIndex.value)
|
||||||
isLooping -> {
|
isLooping -> {
|
||||||
mediaPlayer.clearLoop()
|
mediaPlayer.clearLoop()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ fun Settings(
|
||||||
) {
|
) {
|
||||||
val prefs: Settings = provideSettings()
|
val prefs: Settings = provideSettings()
|
||||||
var hasCountIn by remember { mutableStateOf(prefs.getBoolean("isCountIn", true)) }
|
var hasCountIn by remember { mutableStateOf(prefs.getBoolean("isCountIn", true)) }
|
||||||
|
var isSmartPointBLoop by remember { mutableStateOf(prefs.getBoolean("smartBpoint", true)) }
|
||||||
var isDynamicEnabled by remember { mutableStateOf(Dynamic.isGloballyEnabled) }
|
var isDynamicEnabled by remember { mutableStateOf(Dynamic.isGloballyEnabled) }
|
||||||
var refreshTrigger by remember { mutableStateOf(0) }
|
var refreshTrigger by remember { mutableStateOf(0) }
|
||||||
val isFullScreenEnabled by sharedScreenModel.isFullScreen.collectAsState()
|
val isFullScreenEnabled by sharedScreenModel.isFullScreen.collectAsState()
|
||||||
|
|
@ -698,6 +699,7 @@ fun Settings(
|
||||||
color = MaterialTheme.colorScheme.surfaceContainerLow,
|
color = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
|
Column {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
@ -716,7 +718,7 @@ fun Settings(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(10.dp)
|
horizontalArrangement = Arrangement.spacedBy(10.dp)
|
||||||
) {
|
) {
|
||||||
if(!isAndroid) {
|
if (!isAndroid) {
|
||||||
val countInStat = if (hasCountIn) "Désactiver" else "Activer"
|
val countInStat = if (hasCountIn) "Désactiver" else "Activer"
|
||||||
Text(
|
Text(
|
||||||
text = countInStat,
|
text = countInStat,
|
||||||
|
|
@ -740,6 +742,53 @@ fun Settings(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
HorizontalDivider(
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f),
|
||||||
|
modifier = Modifier.padding(vertical = 4.dp)
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "A-B lopping intelligent",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier.padding(bottom = 8.dp)
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(10.dp)
|
||||||
|
) {
|
||||||
|
if(!isAndroid) {
|
||||||
|
val stat = if (isSmartPointBLoop) "Désactiver" else "Activer"
|
||||||
|
Text(
|
||||||
|
text = stat,
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Switch(
|
||||||
|
checked = isSmartPointBLoop,
|
||||||
|
onCheckedChange = { enabled ->
|
||||||
|
isSmartPointBLoop = enabled
|
||||||
|
prefs.putBoolean("smartBpoint", enabled)
|
||||||
|
},
|
||||||
|
colors = SwitchDefaults.colors(
|
||||||
|
checkedThumbColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
checkedTrackColor = MaterialTheme.colorScheme.primary,
|
||||||
|
uncheckedThumbColor = MaterialTheme.colorScheme.outline,
|
||||||
|
uncheckedTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||||
|
uncheckedBorderColor = MaterialTheme.colorScheme.outline
|
||||||
|
),
|
||||||
|
modifier = Modifier.size(45.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -444,6 +444,36 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
||||||
_activeIndex.value = clamped
|
_activeIndex.value = clamped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _loopingState = MutableStateFlow(Pair(-1, -1))
|
||||||
|
val loopingState: StateFlow<Pair<Int, Int>> = _loopingState.asStateFlow()
|
||||||
|
|
||||||
|
fun updateLoopingState(a: Int, b: Int) {
|
||||||
|
_loopingState.value = Pair(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateSmartPointB(rawPointB: Int, isZeroIndexed: Boolean = false): Int {
|
||||||
|
val currentMeasure = _measure.value
|
||||||
|
val numerator = currentMeasure.substringBefore("/").toIntOrNull() ?: 4
|
||||||
|
|
||||||
|
val multiple = when (numerator) {
|
||||||
|
2, 4, 8, 10 -> 4
|
||||||
|
3, 6, 9, 12 -> 3
|
||||||
|
else -> 2
|
||||||
|
}
|
||||||
|
|
||||||
|
val remainder = rawPointB % multiple
|
||||||
|
var finalPointB = if (remainder == 0) {
|
||||||
|
rawPointB
|
||||||
|
} else {
|
||||||
|
rawPointB + (multiple - remainder)
|
||||||
|
}
|
||||||
|
if (isZeroIndexed) {
|
||||||
|
finalPointB -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalPointB
|
||||||
|
}
|
||||||
private val _gridCount = MutableStateFlow(0)
|
private val _gridCount = MutableStateFlow(0)
|
||||||
val gridCount: StateFlow<Int> = _gridCount.asStateFlow()
|
val gridCount: StateFlow<Int> = _gridCount.asStateFlow()
|
||||||
|
|
||||||
|
|
@ -681,7 +711,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
||||||
_isPos.value = false
|
_isPos.value = false
|
||||||
player.play()
|
player.play()
|
||||||
player.setVolume(_volumeLevel.value)
|
player.setVolume(_volumeLevel.value)
|
||||||
if(currentPos.value == 0f) {
|
if (currentPos.value == 0f && _loopingState.value.first == -1) {
|
||||||
player.seekTo(0)
|
player.seekTo(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -749,6 +779,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
||||||
setTranspositionInterval(0)
|
setTranspositionInterval(0)
|
||||||
setTranspToC(0)
|
setTranspToC(0)
|
||||||
setSilentDurBefr(0)
|
setSilentDurBefr(0)
|
||||||
|
updateLoopingState(0, 0)
|
||||||
_midiMarkersList.value = emptyList()
|
_midiMarkersList.value = emptyList()
|
||||||
_tuoTimestamps.value = emptyList()
|
_tuoTimestamps.value = emptyList()
|
||||||
_searchTitle.value = ""
|
_searchTitle.value = ""
|
||||||
|
|
|
||||||
|
|
@ -639,7 +639,7 @@ actual class FMediaPlayer actual constructor(
|
||||||
|
|
||||||
//Soufflet
|
//Soufflet
|
||||||
step.hairPin != null -> {
|
step.hairPin != null -> {
|
||||||
val resolution = sequencer?.sequence?.resolution?.toLong() ?: 480L
|
val resolution = sequencer?.sequence?.resolution?.toLong() ?: 60L
|
||||||
val gridDistance = (step.hairPinEndGrid - step.gridIndex).coerceAtLeast(1)
|
val gridDistance = (step.hairPinEndGrid - step.gridIndex).coerceAtLeast(1)
|
||||||
val beatDurationMs = (60_000L / targetBpm).toLong()
|
val beatDurationMs = (60_000L / targetBpm).toLong()
|
||||||
val durationMs = (gridDistance * beatDurationMs).coerceIn(200L, 5000L)
|
val durationMs = (gridDistance * beatDurationMs).coerceIn(200L, 5000L)
|
||||||
|
|
@ -717,8 +717,8 @@ actual class FMediaPlayer actual constructor(
|
||||||
syncJob = playerScope.launch(Dispatchers.Default) {
|
syncJob = playerScope.launch(Dispatchers.Default) {
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
if (sequencer?.isRunning == true) {
|
if (sequencer?.isRunning == true) {
|
||||||
val resolution = sequencer?.sequence?.resolution?.toLong() ?: 480L
|
val gridStep = 60L
|
||||||
val gridIndex = (sequencer!!.tickPosition / resolution).toInt()
|
val gridIndex = (sequencer!!.tickPosition / gridStep).toInt()
|
||||||
sharedScreenModel.updateActiveIndexByIndex(gridIndex)
|
sharedScreenModel.updateActiveIndexByIndex(gridIndex)
|
||||||
}
|
}
|
||||||
delay(20)
|
delay(20)
|
||||||
|
|
@ -726,8 +726,8 @@ actual class FMediaPlayer actual constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actual fun seekToGrid(gridIndex: Int) {
|
actual fun seekToGrid(gridIndex: Int) {
|
||||||
val resolution = sequencer?.sequence?.resolution?.toDouble() ?: 480.0
|
val gridStep = 60.0
|
||||||
val tick = (gridIndex.toDouble() * resolution).toLong()
|
val tick = (gridIndex.toDouble() * gridStep).toLong()
|
||||||
sequencer?.tickPosition = tick
|
sequencer?.tickPosition = tick
|
||||||
|
|
||||||
applyBpm()
|
applyBpm()
|
||||||
|
|
@ -746,7 +746,17 @@ actual class FMediaPlayer actual constructor(
|
||||||
}
|
}
|
||||||
actual fun play(){
|
actual fun play(){
|
||||||
if (sequencer?.isOpen == true) {
|
if (sequencer?.isOpen == true) {
|
||||||
val currentTick = sequencer?.tickPosition ?: 0L
|
var currentTick = sequencer?.tickPosition ?: 0L
|
||||||
|
|
||||||
|
if (isLoopingAB && pointA != -1L) {
|
||||||
|
if (currentTick < pointA || currentTick >= pointB) {
|
||||||
|
val gridStep = 60.0
|
||||||
|
val gridIndexA = (pointA / gridStep).toInt()
|
||||||
|
seekToGrid(gridIndexA)
|
||||||
|
currentTick = sequencer?.tickPosition ?: pointA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (currentTick == 0L) {
|
if (currentTick == 0L) {
|
||||||
resetNavigationFlags()
|
resetNavigationFlags()
|
||||||
/*targetBpm = initialBpm
|
/*targetBpm = initialBpm
|
||||||
|
|
@ -754,7 +764,11 @@ actual class FMediaPlayer actual constructor(
|
||||||
boundModel?.setBpmFlow(initialBpm)*/
|
boundModel?.setBpmFlow(initialBpm)*/
|
||||||
}
|
}
|
||||||
val countInEnable = prefs.getBoolean("isCountIn", true)
|
val countInEnable = prefs.getBoolean("isCountIn", true)
|
||||||
if (currentTick == 0L && countInEnable) {
|
|
||||||
|
val isStartingFromBeginning = (currentTick == 0L)
|
||||||
|
val isStartingFromLoopA = (isLoopingAB && currentTick == pointA)
|
||||||
|
|
||||||
|
if ((isStartingFromBeginning || isStartingFromLoopA) && countInEnable) {
|
||||||
playCountIn()
|
playCountIn()
|
||||||
} else {
|
} else {
|
||||||
startMidiPlayback()
|
startMidiPlayback()
|
||||||
|
|
@ -928,12 +942,24 @@ actual class FMediaPlayer actual constructor(
|
||||||
// println("la volume $level")
|
// println("la volume $level")
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun setPointA() {
|
actual fun setPointA(grid: Int) {
|
||||||
pointA = sequencer?.tickPosition ?: 0L
|
val gridStep = 60.0
|
||||||
|
val tick = (grid.toDouble() * gridStep).toLong()
|
||||||
|
pointA = tick
|
||||||
|
|
||||||
|
val currentB = sharedScreenModel.loopingState.value.second
|
||||||
|
sharedScreenModel.updateLoopingState(grid, currentB)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun setPointB() {
|
actual fun setPointB(grid: Int) {
|
||||||
pointB = sequencer?.tickPosition ?: 0L
|
val gridStep = 60.0
|
||||||
|
val tick = (((grid + 1).toDouble() * gridStep) - 1.0).toLong()
|
||||||
|
pointB = tick.coerceAtLeast(0L)
|
||||||
|
|
||||||
|
val currentA = sharedScreenModel.loopingState.value.first
|
||||||
|
sharedScreenModel.updateLoopingState(currentA, grid)
|
||||||
|
seekToGrid(currentA)
|
||||||
|
|
||||||
if (pointB > pointA && pointA != -1L) {
|
if (pointB > pointA && pointA != -1L) {
|
||||||
isLoopingAB = true
|
isLoopingAB = true
|
||||||
startABMonitor()
|
startABMonitor()
|
||||||
|
|
@ -942,6 +968,8 @@ actual class FMediaPlayer actual constructor(
|
||||||
|
|
||||||
actual fun clearLoop() {
|
actual fun clearLoop() {
|
||||||
isLoopingAB = false
|
isLoopingAB = false
|
||||||
|
abJob?.cancel()
|
||||||
|
sharedScreenModel.updateLoopingState(-1, -1)
|
||||||
pointA = -1L
|
pointA = -1L
|
||||||
pointB = -1L
|
pointB = -1L
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue