diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt index 00cf015..321f2bd 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt @@ -211,10 +211,24 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode val isDC = markerText.contains(Regex("""D\.?C\.?""")) val isDS = markerText.contains(Regex("""D\.?S\.?""")) + val isFarany = markerText.trim().equals("Farany", ignoreCase = true) var resultMarker: MidiMarkers = marker - - if (isDC) { + if(isFarany) { + var forwardIndex = index + while (forwardIndex < tuos.size) { + val sep = tuos.getOrNull(forwardIndex)?.sep0 ?: "" + if (sep == "/") { + resultMarker = marker.copy( + gridIndex = forwardIndex - 1, + marker = "Farany_GROUP_PART" + ) + println("Farany finalisé : grille $index → ${forwardIndex - 1}") + break + } + forwardIndex++ + } + } else if (isDC) { var forwardIndex = index while (forwardIndex < tuos.size) { val currentTuo = tuos.getOrNull(forwardIndex) @@ -299,7 +313,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode // _isPos.value = true // _isPlay.value = false _currentPos.value = 0f - seekTo(0f) + seekToGrid(0) _mediaPlayer?.stop() setDcDone(false) setDsDone(false) diff --git a/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt b/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt index 0f3c284..a9490fe 100644 --- a/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt +++ b/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt @@ -3,16 +3,13 @@ package mg.dot.feufaro.midi import SharedScreenModel import kotlinx.coroutines.* import mg.dot.feufaro.getConfigDirectoryPath -import mg.dot.feufaro.viewmodel.MidiMarkers import java.io.File import java.util.prefs.Preferences import javax.sound.midi.MetaMessage import javax.sound.midi.MidiSystem import javax.sound.midi.Sequence import javax.sound.midi.Sequencer -import javax.sound.midi.ShortMessage import javax.sound.midi.Synthesizer -import javax.sound.midi.Track import javax.sound.sampled.AudioSystem import javax.sound.sampled.FloatControl @@ -59,6 +56,8 @@ actual class FMediaPlayer actual constructor( val hairPinEndGrid: Int = -1, val hairPinFromFactor: Float = 1.0f, val hairPinToFactor: Float = 1.0f, + val isFin: Boolean = false, // Farany D.C. + var finActive: Boolean =false, ) private val navigationSteps = mutableListOf() @@ -84,9 +83,13 @@ actual class FMediaPlayer actual constructor( applyVoiceStates() sequencer?.addMetaEventListener { meta -> if(meta.type == 47){ + val totalGrids = (sequencer!!.sequence.tickLength / sequencer!!.sequence.resolution).toInt() + val currentGrid = (sequencer!!.tickPosition / sequencer!!.sequence.resolution).toInt() val pendingDc = getPendingDcStep() - if (pendingDc != null) { + val isDcNearEnd = pendingDc != null && (totalGrids - pendingDc.gridIndex) <= 2 + + if (isDcNearEnd && pendingDc != null) { println("onFinished : DC pending → saut direct vers ${pendingDc.targetGrid}") pendingDc.alreadyDone = true seekToGrid(pendingDc.targetGrid) @@ -281,6 +284,27 @@ actual class FMediaPlayer actual constructor( println("Lien DC créé : $marker à $indx vers le début") } + // Farany + marker.trim().equals("Farany_GROUP_PART", ignoreCase = true) -> { + val hasDcAfter = metadataList.any { (_, gi, _, _, mk, _, _, _) -> + (gi ?: 0) > currentIndex && + (dcGPattern.matches(mk.trim()) || + dcRegex.matches(mk.trim())) + } + + if (hasDcAfter) { + navigationSteps.add( + NavigationStep( + marker = marker, + gridIndex = currentIndex, + targetGrid = last_grid, + isFin = true, + finActive = false + ) + ) + println("Farany mémorisé à la grille $currentIndex") + } + } // velocité extractDynamic(marker) != null && marker.trim() != "=" -> { val dyn = extractDynamic(marker) ?: return@forEach @@ -362,6 +386,7 @@ actual class FMediaPlayer actual constructor( step.hairPin == null && step.dynamic == null && !step.isHold + && !step.isFin } } private fun startNavigationMonitor(sharedScreenModel: SharedScreenModel) { @@ -389,6 +414,19 @@ actual class FMediaPlayer actual constructor( if (step != null) { when { + step.isFin -> { + if(step.finActive) { + step.alreadyDone = true +// println("Farany passage → FIN à grille $currentIndex") + sequencer?.stop() + sequencer?.tempoFactor = 1f + synthetizer?.channels?.forEach { + it?.controlChange(64, 0) + it?.controlChange(123, 0) + } + onFinished() + } + } // Point d'orgue step.isHold -> { val currentBpm = sequencer?.tempoInBPM ?: targetBpm @@ -443,6 +481,14 @@ actual class FMediaPlayer actual constructor( seekToGrid(step.targetGrid) synthetizer?.channels?.forEach { it?.controlChange(64, 0) } sequencer?.tempoFactor = 1f + + navigationSteps.filter { + it.isFin + }.forEach { + it.finActive = true + it.alreadyDone = false + } + if (sequencer?.isRunning == false) { sequencer?.tempoInBPM = targetBpm sequencer?.start()