Implement Rit. & Rall on playing Midi
This commit is contained in:
parent
5ad97f4f21
commit
b605fc87fc
2 changed files with 126 additions and 0 deletions
|
|
@ -213,6 +213,8 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
|||
val isDS = markerText.contains(Regex("""D\.?S\.?"""))
|
||||
val isFarany = markerText.trim().equals("Farany", ignoreCase = true)
|
||||
|
||||
val isRit = Regex("""rit\.?|ritard\.?|ritenuto\.?|ritardando""", RegexOption.IGNORE_CASE)
|
||||
|
||||
var resultMarker: MidiMarkers = marker
|
||||
if(isFarany) {
|
||||
var forwardIndex = index
|
||||
|
|
@ -287,6 +289,24 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
|||
forwardIndex++
|
||||
}
|
||||
}
|
||||
} else if(isRit.containsMatchIn(markerText)) {
|
||||
var forwardIndex = index
|
||||
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 {
|
||||
marker
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,9 @@ actual class FMediaPlayer actual constructor(
|
|||
private var abJob: Job? = null
|
||||
|
||||
private var navigationJob: Job? = null
|
||||
|
||||
private var isInTempoChange: Boolean = false
|
||||
private var tempoChangeJob: Job? = null
|
||||
private data class NavigationStep(
|
||||
val marker: String,
|
||||
val gridIndex: Int, // L'ancre absolue
|
||||
|
|
@ -61,6 +64,12 @@ actual class FMediaPlayer actual constructor(
|
|||
val hairPinToFactor: Float = 1.0f,
|
||||
val isFin: Boolean = false, // Farany D.C.
|
||||
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>()
|
||||
|
||||
|
|
@ -195,11 +204,54 @@ actual class FMediaPlayer actual constructor(
|
|||
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) {
|
||||
val metadataList = sharedScreenModel.getFullMarkers()
|
||||
navigationSteps.clear()
|
||||
var lastSegno = 0
|
||||
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) ->
|
||||
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")
|
||||
}
|
||||
// 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
|
||||
dsGPattern.matches(marker.trim())-> {
|
||||
val target = if (lastSegno > 0) lastSegno else 0
|
||||
|
|
@ -415,6 +498,9 @@ actual class FMediaPlayer actual constructor(
|
|||
}
|
||||
|
||||
if (step != null) {
|
||||
if (Math.abs(sequencer!!.tempoInBPM - targetBpm) > 0.1 && !isInTempoChange) {
|
||||
forceTempo(targetBpm.toDouble())
|
||||
}
|
||||
when {
|
||||
step.isFin -> {
|
||||
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
|
||||
step.dynamic != null -> {
|
||||
applyDynamic(step.dynamic)
|
||||
|
|
@ -601,6 +706,7 @@ actual class FMediaPlayer actual constructor(
|
|||
navigationSteps.clear()
|
||||
clearLoop()
|
||||
currentDynamicFactor = Dynamic.MF.factor
|
||||
resetTempoToNormal()
|
||||
// release()
|
||||
}
|
||||
actual fun getDuration(): Long {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue