Implement Rit. & Rall on playing Midi
This commit is contained in:
parent
5ad97f4f21
commit
ae66feefec
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 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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue