Implement Rit. & Rall on playing Midi

This commit is contained in:
hasinarak3@gmail.com 2026-03-17 16:08:11 +03:00
parent 5ad97f4f21
commit ae66feefec
2 changed files with 126 additions and 0 deletions

View file

@ -213,6 +213,8 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
val isDS = markerText.contains(Regex("""D\.?S\.?""")) val isDS = markerText.contains(Regex("""D\.?S\.?"""))
val isFarany = markerText.trim().equals("Farany", ignoreCase = true) val isFarany = markerText.trim().equals("Farany", ignoreCase = true)
val isRit = Regex("""rit\.?|ritard\.?|ritenuto\.?|ritardando""", RegexOption.IGNORE_CASE)
var resultMarker: MidiMarkers = marker var resultMarker: MidiMarkers = marker
if(isFarany) { if(isFarany) {
var forwardIndex = index var forwardIndex = index
@ -287,6 +289,24 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
forwardIndex++ forwardIndex++
} }
} }
} else if(isRit.containsMatchIn(markerText)) {
var forwardIndex = index + 1
var foundSeparator = false
while (forwardIndex < tuos.size) {
val sep = tuos.getOrNull(forwardIndex)?.sep0 ?: ""
if (sep == "/") {
resultMarker = marker.copy(
gridIndex = index,
lastCallerMarker = forwardIndex - 1,
marker = "Ritenuto"
)
println("Rit finalisé : grille $index${forwardIndex - 1}")
foundSeparator = true
break
}
forwardIndex++
}
} else { } else {
marker marker
} }

View file

@ -46,6 +46,9 @@ actual class FMediaPlayer actual constructor(
private var abJob: Job? = null private var abJob: Job? = null
private var navigationJob: Job? = null private var navigationJob: Job? = null
private var isInTempoChange: Boolean = false
private var tempoChangeJob: Job? = null
private data class NavigationStep( private data class NavigationStep(
val marker: String, val marker: String,
val gridIndex: Int, // L'ancre absolue val gridIndex: Int, // L'ancre absolue
@ -61,6 +64,12 @@ actual class FMediaPlayer actual constructor(
val hairPinToFactor: Float = 1.0f, val hairPinToFactor: Float = 1.0f,
val isFin: Boolean = false, // Farany D.C. val isFin: Boolean = false, // Farany D.C.
var finActive: Boolean =false, var finActive: Boolean =false,
// rit & rall
val isTempoChange: Boolean = false,
val tempoType: String = "",
val endGridForTempo: Int = -1,
val targetTempoMultiplier: Float = 0.6f
) )
private val navigationSteps = mutableListOf<NavigationStep>() private val navigationSteps = mutableListOf<NavigationStep>()
@ -195,11 +204,54 @@ actual class FMediaPlayer actual constructor(
println("applyCrescendo terminé → factor=$currentDynamicFactor") println("applyCrescendo terminé → factor=$currentDynamicFactor")
} }
} }
private fun applyTempoChange(
startBpm: Float,
endBpm: Float,
durationMs: Long,
tempoType: String
) {
tempoChangeJob?.cancel()
isInTempoChange = true
tempoChangeJob = playerScope.launch {
val steps = 50
val stepMs = (durationMs / steps).coerceAtLeast(20L)
val startTime = System.currentTimeMillis()
for (i in 1..steps) {
val elapsed = System.currentTimeMillis() - startTime
val progress = (elapsed.toFloat() / durationMs).coerceIn(0f, 1f)
val smooth = progress * progress * (3f - 2f * progress) // ease-in-out
val currentBpm = startBpm + (endBpm - startBpm) * smooth
sequencer?.tempoInBPM = currentBpm
delay(stepMs)
if (!isActive) break
}
sequencer?.tempoInBPM = endBpm
isInTempoChange = false
}
}
private fun resetTempoToNormal() {
if (sequencer?.tempoInBPM != targetBpm) {
// println("Retour au tempo normal: ${sequencer?.tempoInBPM} → $targetBpm BPM")
applyTempoChange(
startBpm = sequencer?.tempoInBPM ?: targetBpm,
endBpm = targetBpm,
durationMs = 800L,
tempoType = "Retour tempo"
)
}
}
private fun prepareNavigation(sharedScreenModel: SharedScreenModel) { private fun prepareNavigation(sharedScreenModel: SharedScreenModel) {
val metadataList = sharedScreenModel.getFullMarkers() val metadataList = sharedScreenModel.getFullMarkers()
navigationSteps.clear() navigationSteps.clear()
var lastSegno = 0 var lastSegno = 0
var lastFactor = Dynamic.MF.factor var lastFactor = Dynamic.MF.factor
val ritRegex = Regex("""rit\.?|ritard\.?|ritenuto\.?|ritardando""", RegexOption.IGNORE_CASE)
val rallRegex = Regex("""rall\.?|rallent\.?|rallentando""", RegexOption.IGNORE_CASE)
metadataList.forEach { (timestamp, gridIndex, template, lastCallerMarker, marker, noteBefore, separat, note) -> metadataList.forEach { (timestamp, gridIndex, template, lastCallerMarker, marker, noteBefore, separat, note) ->
val currentIndex = gridIndex ?: 0 val currentIndex = gridIndex ?: 0
@ -230,6 +282,37 @@ actual class FMediaPlayer actual constructor(
) )
println("Point d'orgue (\uD834\uDD10) mémorisée au grille n° $gridIndex") println("Point d'orgue (\uD834\uDD10) mémorisée au grille n° $gridIndex")
} }
// Rit ...
ritRegex.containsMatchIn(marker) -> {
val endGrid = if(lastCallerMarker == 0) last_grid else lastCallerMarker
navigationSteps.add(
NavigationStep(
marker = marker,
gridIndex = currentIndex,
isTempoChange = true,
tempoType = "rit",
endGridForTempo = endGrid,
targetTempoMultiplier = 0.55f // 55% du tempo initial
)
)
println("Ritardando mémorisé à grille $currentIndex jusqu'à $endGrid")
}
rallRegex.containsMatchIn(marker) -> {
val endGrid = last_grid
navigationSteps.add(
NavigationStep(
marker = marker,
gridIndex = currentIndex,
isTempoChange = true,
tempoType = "rall",
endGridForTempo = endGrid,
targetTempoMultiplier = 0.50f // 50% du tempo initial
)
)
println("Rallentando mémorisé à grille $currentIndex jusqu'à $endGrid")
}
// DS // DS
dsGPattern.matches(marker.trim())-> { dsGPattern.matches(marker.trim())-> {
val target = if (lastSegno > 0) lastSegno else 0 val target = if (lastSegno > 0) lastSegno else 0
@ -415,6 +498,9 @@ actual class FMediaPlayer actual constructor(
} }
if (step != null) { if (step != null) {
if (Math.abs(sequencer!!.tempoInBPM - targetBpm) > 0.1 && !isInTempoChange) {
forceTempo(targetBpm.toDouble())
}
when { when {
step.isFin -> { step.isFin -> {
if(step.finActive) { if(step.finActive) {
@ -463,6 +549,25 @@ actual class FMediaPlayer actual constructor(
) )
} }
//Rit | Rall
step.isTempoChange -> {
step.alreadyDone = true
val currentBpm = sequencer?.tempoInBPM ?: targetBpm
val endBpm = targetBpm * step.targetTempoMultiplier
val gridDistance = (step.endGridForTempo - step.gridIndex).coerceAtLeast(1)
val beatDurationMs = (60_000L / targetBpm).toLong()
val durationMs = (gridDistance * beatDurationMs).coerceIn(1000L, 8000L)
// println("${step.tempoType.uppercase()} : Grille ${step.gridIndex} → ${step.endGridForTempo}")
// println(" ${currentBpm} BPM → $endBpm BPM sur ${durationMs}ms")
applyTempoChange(
startBpm = currentBpm,
endBpm = endBpm,
durationMs = durationMs,
tempoType = step.tempoType
)
}
//velocity //velocity
step.dynamic != null -> { step.dynamic != null -> {
applyDynamic(step.dynamic) applyDynamic(step.dynamic)
@ -601,6 +706,7 @@ actual class FMediaPlayer actual constructor(
navigationSteps.clear() navigationSteps.clear()
clearLoop() clearLoop()
currentDynamicFactor = Dynamic.MF.factor currentDynamicFactor = Dynamic.MF.factor
resetTempoToNormal()
// release() // release()
} }
actual fun getDuration(): Long { actual fun getDuration(): Long {