diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt index e0f61bc..d342c63 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt @@ -593,7 +593,34 @@ fun LazyVerticalGridTUO( -1 }*/ val measures = tuoList.drop(1).chunked(gridColumnCount) - val metadataList = mutableListOf() + // Avant column affichage: + val metadataList = remember(tuoList) { + tuoList.drop(1).mapIndexedNotNull { globalIndex, oneTUO -> + val markerText = oneTUO.pTemplate.markerToString() + if (markerText.isNotEmpty()) { + val myTimestamp = sharedScreenModel.tuoTimestamps.value.getOrElse(globalIndex) { 0L } + MidiMarkers( + myTimestamp, + globalIndex, + oneTUO.pTemplate.template, + oneTUO.pTemplate.lastCalledMarker, + markerText, + oneTUO.prevTUO?.pTemplate?.template ?: "", + oneTUO.sep0, + oneTUO.tuNotes.getOrNull(1).toString() + ) + + } else null + }.distinctBy { it.gridIndex } + } + + // Envoyer les données au ViewModel une seule fois + LaunchedEffect(metadataList) { + if (metadataList.isNotEmpty()) { + println("Mise à jour MidiData avec ${metadataList.size}") + sharedScreenModel.updateAndFinalizeMidiData(metadataList) + } + } Column( modifier = Modifier.fillMaxWidth() ){ @@ -699,20 +726,6 @@ fun LazyVerticalGridTUO( val myTimestamp = sharedScreenModel.tuoTimestamps.value.getOrElse(globalIndex) { 0L } - if(oneTUO.pTemplate.markerToString() != "") { - val nott = oneTUO.tuNotes.getOrNull(1) - metadataList.add( - MidiMarkers( - myTimestamp, - oneTUO.pTemplate.template, - oneTUO.pTemplate.lastCalledMarker, - oneTUO.pTemplate.markerToString(), - oneTUO.prevTUO!!.pTemplate!!.template, - oneTUO.sep0, - oneTUO.tuNotes.getOrNull(1).toString() - ) - ) - } Box(modifier = Modifier.weight(1f) .combinedClickable( onClick = { @@ -723,8 +736,9 @@ fun LazyVerticalGridTUO( } , onDoubleClick = { val m = sharedScreenModel.getFullMarkers() - m.forEach { (timestamp, template, lastCallerMarker, marker, noteBefore, separat, note) -> - println("Allmarker : $marker in $timestamp ms & note= $note & nBefore =$noteBefore & separateur = $separat") + m.forEach { (timestamp, gridIndex, template, lastCallerMarker, marker, noteBefore, separat, note) -> + println("Allmarker $marker in $gridIndex on $timestamp ms") + // & note= $note & nBefore =$noteBefore & separateur = $separat } } )) { diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/MidiMetadata.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/MidiMetadata.kt index a63027d..8fc62b5 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/MidiMetadata.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/MidiMetadata.kt @@ -2,6 +2,7 @@ package mg.dot.feufaro.viewmodel data class MidiMarkers( val timestamp: Long, + val gridIndex: Int? = 0, val template: String, val lastCallerMarker: Int, val marker: String, 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 d9d7d8f..b32de9d 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt @@ -6,7 +6,6 @@ import cafe.adriel.voyager.core.model.ScreenModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -143,9 +142,15 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode private val _midiMarkersList = MutableStateFlow>(emptyList()) val midiMarkersList: StateFlow> = _midiMarkersList + private val _activeIndex = MutableStateFlow(-1) + val activeIndex: StateFlow = _activeIndex.asStateFlow() - fun updateMidiData(newList: List) { - _midiMarkersList.value = newList + fun updateActiveIndex(currentPosMs: Long) { + val currentPosMicros = currentPosMs * 1000 + val index = _tuoTimestamps.value.indexOfLast { it <= currentPosMicros } + if (_activeIndex.value != index) { + _activeIndex.value = index.coerceAtLeast(0) + } } fun getFullMarkers(): List { @@ -165,58 +170,90 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode return _dsDone.value } - fun finalizeMarkers() { - val timestamps = _tuoTimestamps.value + fun getTotalGridCount(): Int { + return _tuoList.value.drop(1).size - 1 + } + fun updateAndFinalizeMidiData(rawList: List) { +// val timestamps = _tuoTimestamps.value val tuos = _tuoList.value.drop(1) - val newMtdList = mutableListOf() - tuos.forEachIndexed { index, tuo -> - val markerText = tuo.pTemplate.markerToString() - if (markerText.isNotEmpty()) { - val ts = timestamps.getOrNull(index) ?: 0L - newMtdList.add( - MidiMarkers( - timestamp = ts, - template = tuo.pTemplate.template, - lastCallerMarker = tuo.pTemplate.lastCalledMarker, - marker = markerText, - noteBefore = tuo.prevTUO!!.pTemplate!!.template, - separat = tuo.sep0, - note = tuo.tuNotes.getOrNull(1).toString() - ) - ) - if(markerText.contains("DC")){ // marquer la fin de mesure après un D.C. - var internIn = index +1 - while (internIn < tuos.size) { - val currentTuo = tuos[internIn] - val currentNote = currentTuo.tuNotes.getOrNull(1)?.toString() ?: "" - val currentSep = currentTuo.sep0 ?: "" + val finalizedList = rawList.map { marker -> + var markerText = marker.marker + val index = marker.gridIndex ?: 0 // On utilise l'index passé par l'UI + val isNearEnd = index >= (tuos.size - 2) - // On se position sur la note apres les tiret "-" ou sur / - if ((currentNote != "―" && currentNote.isNotBlank()) || (currentSep == "/")) { - val currentTs = timestamps.getOrNull(internIn) ?: 0L - println("Dernier '-' passé. Nouvelle note '$currentNote' trouvée à l'index $internIn (Position du saut)") - newMtdList.clear() - newMtdList.add( - MidiMarkers( - timestamp = currentTs, - template = currentTuo.pTemplate.template, - lastCallerMarker = currentTuo.pTemplate.lastCalledMarker, - marker = "DC_GROUP_PART", - noteBefore = currentTuo.prevTUO!!.pTemplate!!.template, - separat = currentTuo.sep0, - note = currentNote - ) + val isDC = markerText.contains(Regex("""D\.?C\.?""")) + val isDS = markerText.contains(Regex("""D\.?S\.?""")) + + var resultMarker: MidiMarkers = marker + + if (isDC) { + var forwardIndex = index + while (forwardIndex < tuos.size) { + val currentTuo = tuos.getOrNull(forwardIndex) + val currentNote = currentTuo?.tuNotes?.getOrNull(1)?.toString() ?: "" + val currentSep = currentTuo?.sep0 ?: "" + +// println("Je suis sur $forwardIndex note $currentNote Sep $currentSep \t condition: ${(currentNote == "―")} || ${(currentSep == "/")}") + // Tant que fin de mesure + if (currentSep == "/") { + resultMarker = marker.copy( + gridIndex = forwardIndex-1, + marker = "${markerText.trim()}_GROUP_PART" + ) + break + } + forwardIndex++ + } + + } else if (isDS && isNearEnd) { + val currentNote = tuos.getOrNull(index)?.tuNotes?.getOrNull(1)?.toString() ?: "" + val nextNote = tuos.getOrNull(index + 1)?.tuNotes?.getOrNull(1)?.toString() ?: "" + + // Cas où le marker et la note suivante sont vides + val isEmptySituation = currentNote.trim().isEmpty() && nextNote.trim().isEmpty() + + if (isEmptySituation) { + // vers arrière une note non vide + var backwardIndex = index - 1 + while (backwardIndex >= 0) { + val note = tuos.getOrNull(backwardIndex)?.tuNotes?.getOrNull(1)?.toString() ?: "" + if (note.trim().isNotEmpty()) { + val cleanText = if (markerText.trim() == "DSFin") "DS" else markerText.trim() + + resultMarker = marker.copy( + gridIndex = backwardIndex, + marker = "${cleanText}_GROUP_PART" ) break } - internIn++ + backwardIndex-- + } + } else { + // vers avant le séparateur "/" + var forwardIndex = index + while (forwardIndex < tuos.size) { + val sep = tuos.getOrNull(forwardIndex)?.sep0 ?: "" + if (sep == "/") { + val cleanText = if (markerText.trim() == "DSFin") "DS" else markerText.trim() + + resultMarker = marker.copy( + gridIndex = forwardIndex, + marker = "${cleanText}_GROUP_PART" + ) + break + } + forwardIndex++ } } + } else { + marker } + resultMarker } - updateMidiData(newMtdList) - println("Markers loaded >> ok") + + _midiMarkersList.value = finalizedList + println("Markers finalisés et mis à jour : ${finalizedList.size}") } fun loadNewSong(newMidiFile: String) { @@ -244,7 +281,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode }) // synchro _mediaPlayer?.requestSync(this) - finalizeMarkers() + //finalizeMarkers() _mediaPlayer?.syncNavigationMonitor(this) } catch(e: Exception) { println("Erreur d'ouverture de mediaPlayer / ") @@ -268,7 +305,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode player.seekTo(0) } } - println("128: Status de isPlay ${_isPlay.value} \nisPos ${_isPos.value} \ncurrentPos ${_currentPos.value} \n volume ${_volumeLevel.value}") +// println("128: Status de isPlay ${_isPlay.value} \nisPos ${_isPos.value} \ncurrentPos ${_currentPos.value} \n volume ${_volumeLevel.value}") } } fun stopMidi() { 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 b5da82e..226ac9c 100644 --- a/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt +++ b/composeApp/src/desktopMain/kotlin/mg/dot/feufaro/midi/MidiPlayer.kt @@ -45,15 +45,16 @@ actual class FMediaPlayer actual constructor( private var navigationJob: Job? = null private data class NavigationStep( - val triggerMs: Long, // DC | DS - val targetMs: Long, // farany, $ ,.. + val marker: String, + val gridIndex: Int, // L'ancre absolue + val targetGrid: Int = 0, val isHold: Boolean = false, // point d'orgue var alreadyDone: Boolean = false, // done var beatInDC: Int = 1, // temps note sur le marker - var sustainNedd: Boolean = true ) private val navigationSteps = mutableListOf() + private var boundModel: SharedScreenModel? = null init { try { sequencer?.open() @@ -69,6 +70,8 @@ actual class FMediaPlayer actual constructor( if (file.exists()) { val mySequence = MidiSystem.getSequence(file) sequencer?.sequence = mySequence + stripTempoEvents(mySequence) + sequencer?.tempoFactor = 1.0f loadVoiceVolumes() applyVoiceStates() sequencer?.addMetaEventListener { meta -> @@ -76,6 +79,15 @@ actual class FMediaPlayer actual constructor( onFinished() } } +/* + sequencer?.addMetaEventListener { meta -> + if (meta.type == 0x51) { // 0x51 "Set Tempo" + if (sequencer?.tempoInBPM != targetBpm) { + println("Tempo MIDI détecté (120), forçage à $targetBpm") + applyBpm() + } + } + }*/ } else { // Créeons une fichier vide au 1er lancement de l'application, après MidiWriterKotlin l'écrasera val f0file = File("${getConfigDirectoryPath()}whawyd3.mid") @@ -83,45 +95,115 @@ actual class FMediaPlayer actual constructor( } } + private fun stripTempoEvents(sequence: Sequence) { + for (track in sequence.tracks) { + val eventsToRemove = mutableListOf() + for (i in 0 until track.size()) { + val event = track.get(i) + val message = event.message + if (message is MetaMessage && message.type == 0x51) { // 0x51 = Set Tempo + eventsToRemove.add(event) + } + } + eventsToRemove.forEach { track.remove(it) } + } + } + private fun applyBpm() { + sequencer?.tempoInBPM = targetBpm + sequencer?.tempoFactor = 1.0f + } + private fun forceTempo(bpm: Double) { + sequencer?.tempoInBPM = bpm.toFloat() + sequencer?.tempoFactor = 1.0f + } private fun resetNavigationFlags() { navigationSteps.forEach { it.alreadyDone = false } } private fun prepareNavigation(sharedScreenModel: SharedScreenModel) { val metadataList = sharedScreenModel.getFullMarkers() navigationSteps.clear() - var holdPos: Long = 0L + var lastSegno = 0 - val currentBpm = sequencer?.tempoInBPM ?: 120f - val beatDurationMs = (60_000 / currentBpm).toLong() + metadataList.forEach { (timestamp, gridIndex, template, lastCallerMarker, marker, noteBefore, separat, note) -> + val currentIndex = gridIndex ?: 0 - metadataList.forEach { (timestamp, template, lastCallerMarker, marker, noteBefore, separat, note) -> - val timeMs = (timestamp / 1000) + val dsRegex = Regex("""D\.?S\.?""") + val dcRegex = Regex("""D\.?C\.?""") + val dsGPattern = Regex("""D\.?S\.?_GROUP_PART""") + val dcGPattern = Regex("""D\.?C\.?_GROUP_PART""") + + val last_grid = sharedScreenModel.getTotalGridCount() when { + marker.contains("$") -> { + lastSegno = currentIndex + println("Cible ($) mémorisée au $lastSegno") + } marker.contains("\uD834\uDD10") -> { - holdPos = timeMs val beat = if(note.contains('•')) 2 else 1 // demi-ton sur .) ou non - navigationSteps.add(FMediaPlayer.NavigationStep(timeMs, -1L, isHold = true, beatInDC = beat)) - println("Point d'orgue (\uD834\uDD10) mémorisée à $holdPos ms") + navigationSteps.add( + FMediaPlayer.NavigationStep( + marker, + currentIndex, + isHold = true, + beatInDC = beat + ) + ) + println("Point d'orgue (\uD834\uDD10) mémorisée au grille n° $gridIndex") } - // Déclencheurs DC - marker == "DC_GROUP_PART" -> { - var needSustaine = false - var trigger = timeMs - (beatDurationMs / 2) + /*// DS + dsGPattern.matches(marker.trim())-> { + val target = if (lastSegno > 0) lastSegno else 0 + var indx = if((last_grid - currentIndex) <= 0) { + currentIndex -1 + } else currentIndex + navigationSteps.add( + NavigationStep( + marker, + gridIndex = indx, + targetGrid = target, + ) + ) + println("Lien créé : $marker à grille n° $indx vers cible $target ........") + } + dsRegex.matches(marker.trim()) || marker == "DSFin" -> { + val target = if (lastSegno > 0) lastSegno else 0 - navigationSteps.add(NavigationStep(trigger, targetMs = 0L, sustainNedd = needSustaine)) - println("Lien créé : $marker à $trigger ms vers cible 0 ms Avec Sustaine? $needSustaine") + navigationSteps.add( + NavigationStep( + marker, + currentIndex, + targetGrid = target + )) + println("Lien DS créé : Saut immédiat à $gridIndex vers Segno $target") + }*/ + + // DC + dcGPattern.matches(marker.trim()) -> { + var indx = if((last_grid - currentIndex) <= 0) { + currentIndex - 1 + } else currentIndex + navigationSteps.add( + NavigationStep( + marker, + indx, + targetGrid = 0 + ) + ) + println("Lien créé : $marker à $indx vers cible 0 ") } - /* Un Dc se place sur une note '-'*/ - marker == "DC" -> { - var needSustaine = false - val trigger = timeMs - // Si le marker si place près de la fin de la partition - if((getDuration() - timeMs) <= beatDurationMs) { - println("Fin très proche") - needSustaine = true - } - navigationSteps.add(NavigationStep(trigger, targetMs = 0, sustainNedd = needSustaine)) - println("Lien créé : $marker à $trigger ms vers le début") + (dcRegex.matches(marker.trim()) && !marker.contains("DC_GROUP_PART")) -> { + println("dernier grille $last_grid") + var indx = if((last_grid - currentIndex) <= 0) { + currentIndex /*- 1*/ + } else currentIndex + navigationSteps.add( + NavigationStep( + marker, + indx, + targetGrid = 0 + ) + ) + println("Lien DC créé : $marker à $indx vers le début") } } } @@ -130,90 +212,101 @@ actual class FMediaPlayer actual constructor( var dcDone = sharedScreenModel.getDcDone() val dsDone = sharedScreenModel.getDsDone() - // la durée d'un temps (noire) - val currentBpm = sequencer?.tempoInBPM ?: 120f - val beatDurationMs = (60_000 / currentBpm).toLong() - - val remainingTime = getDuration() - getCurrentPosition() - navigationJob?.cancel() - navigationJob = playerScope.launch { - while (isActive) { - if (sequencer?.isRunning == true) { - val currentPos = getCurrentPosition() - val duration = getDuration() - val measure = sharedScreenModel.measure.value - val firstVal = measure.substringBefore("/") - val mesureNum = firstVal.toIntOrNull() ?: 4 + navigationJob = playerScope.launch(Dispatchers.Default) { + sharedScreenModel.activeIndex.collect { currentIndex -> + + val availableIndices = navigationSteps.map { it.gridIndex } + println("bpm:$targetBpm _ ${sequencer?.tempoInBPM}") + println("MONITOR : Reçu Index $currentIndex | Index en mémoire : $availableIndices ") + + if (sequencer?.isRunning == true) { + if (Math.abs(sequencer!!.tempoInBPM - targetBpm) > 0.1) { + forceTempo(targetBpm.toDouble()) + } + if (currentIndex < 0) return@collect - // On cherche si on est sur un point de saut val step = navigationSteps.find { - currentPos >= it.triggerMs && - currentPos < it.triggerMs + 800 && + it.gridIndex == currentIndex && !it.alreadyDone } + if (step != null) { - if (currentPos < step.triggerMs + 800) { + // Point d'orgue + if (step.isHold) { + val currentBpm = sequencer?.tempoInBPM ?: targetBpm + val beatMs = (60_000 / currentBpm).toLong() + val holdDuration = if (step.beatInDC == 2) beatMs / 2 else beatMs * 2 + + println("POINT D'ORGUE sur Grille $currentIndex | Durée: ${holdDuration}ms") + + synthetizer?.channels?.forEach { it?.controlChange(64, 127) } + val previousFactor = sequencer?.tempoFactor ?: 1.0f + sequencer?.tempoFactor = 0.0001f + + delay(holdDuration) + + sequencer?.tempoFactor = previousFactor + synthetizer?.channels?.forEach { it?.controlChange(64, 0) } + } else { + // $ dc ds ... step.alreadyDone = true - val timeToFinish = duration - currentPos - val isCloseToEnd = timeToFinish < (beatDurationMs * 1.5).toLong() -// println("time to finish = $timeToFinish et isClo $isCloseToEnd") - // point d'orgue - if (step.isHold) { - synthetizer?.channels?.forEach { it?.controlChange(64, 127) } - sequencer?.tempoFactor = 0.0001f + val currentBpm = targetBpm + val beatDuration = (60_000 / currentBpm).toLong() + println("avant de sauter bpm=$targetBpm") - if(step.beatInDC == 1){ - delay(beatDurationMs * 2) - } else { //démi-ton sur le point d'orgue - delay(beatDurationMs) - } - println("Est-ce un demi-ton ? ${step.beatInDC} Le delay /2 est : ${(beatDurationMs / step.beatInDC) * 2} et pour 1 ${beatDurationMs * 2}") - synthetizer?.channels?.forEach { it?.controlChange(64, 0) } - sequencer?.tempoFactor = currentTempo - step.alreadyDone=false - } else { - step.alreadyDone = true + synthetizer?.channels?.forEach { it?.controlChange(64, 127) } + sequencer?.tempoFactor = 0.0001f + delay(beatDuration) - // Sustain - if(step.sustainNedd) { - synthetizer?.channels?.forEach { it?.controlChange(64, 127) } - sequencer?.tempoFactor = 0.0001f - - delay(beatDurationMs * (mesureNum/2)) - - synthetizer?.channels?.forEach { - it?.controlChange(64, 0) - it?.controlChange(123, 0) - } - - delay(50) // Silence de respiration - - sequencer?.tempoFactor = currentTempo - setVolume(currentGlobalVolume) - } - seekTo(step.targetMs) - - if (sequencer?.isRunning == false) sequencer?.start() - -// delay(500) +// println("et là je saut vers ${step.targetGrid}") + seekToGrid(step.targetGrid) + synthetizer?.channels?.forEach { it?.controlChange(64, 0) } + sequencer?.tempoFactor = 1f + if (sequencer?.isRunning == false) { + sequencer?.tempoInBPM = targetBpm + sequencer?.start() } +// println("Après de sauter bpm=$targetBpm") } - delay(300) } } - delay(100) } } } + private var syncJob: Job? = null + + private fun startSyncLoop(sharedScreenModel: SharedScreenModel) { + syncJob?.cancel() + syncJob = playerScope.launch(Dispatchers.Default) { + while (isActive) { + if (sequencer?.isRunning == true) { + val posMs = (sequencer?.microsecondPosition ?: 0L) / 1000 + sharedScreenModel.updateActiveIndex(posMs) + } + delay(20) + } + } + } + private fun seekToGrid(gridIndex: Int) { + val resolution = sequencer?.sequence?.resolution?.toDouble() ?: 480.0 + val tick = gridIndex.toDouble() * resolution + + val bpm = sequencer?.tempoInBPM ?: targetBpm + val usPerTick = (60_000_000.0 / bpm) / resolution + val targetMicros = (tick * usPerTick).toLong() + + sequencer?.microsecondPosition = targetMicros + } actual fun play(){ - if (!sequencer!!.isOpen){ - sequencer!!.open() + if (sequencer?.isOpen == true){ + applyBpm() + sequencer?.start() + println("La sequence vient d etre lancé ${sequencer?.isRunning}") +// sequencer!!.open() } - sequencer?.start() - println("La sequence vient d etre lancé ${sequencer?.isRunning}") } actual fun pause(){ sequencer?.stop() @@ -226,14 +319,16 @@ actual class FMediaPlayer actual constructor( } actual fun syncNavigationMonitor(sharedScreenModel: SharedScreenModel) { + this.boundModel = sharedScreenModel prepareNavigation(sharedScreenModel) startNavigationMonitor(sharedScreenModel) + startSyncLoop(sharedScreenModel) } fun syncTuoWithMidi(sequence: Sequence, sharedScreenModel: SharedScreenModel) { val timestamps = mutableListOf() val resolution = sequence.resolution.toDouble() - val bpm = sequencer?.tempoInBPM?.toDouble() ?: 120.0 + val bpm = targetBpm/*sequencer?.tempoInBPM?.toDouble() ?: 120.0*/ val usPerTick = (60_000_000.0 / bpm) / resolution // détecter par 60 ticks càd par un temp (noir) @@ -283,6 +378,7 @@ actual class FMediaPlayer actual constructor( } actual fun seekTo(position: Long) { sequencer?.microsecondPosition = position * 1000 + applyBpm() } actual fun release() { sequencer?.close() @@ -316,7 +412,7 @@ actual class FMediaPlayer actual constructor( } catch (e: Exception){ e.printStackTrace() } - println("la volume $level") +// println("la volume $level") } actual fun setPointA() { @@ -376,7 +472,7 @@ actual class FMediaPlayer actual constructor( channels[i].controlChange(123, 0) } } - println("SATB $i Volumes: ${voiceVolumes[i]}") +// println("SATB $i Volumes: ${voiceVolumes[i]}") } } } catch (e: Exception) { e.printStackTrace() } @@ -405,21 +501,6 @@ actual class FMediaPlayer actual constructor( } println("Tempo réglé à : $bpm BPM") } - fun getTempo(): Float = currentTempo - private val MS8PER_NOTE = 500L - - fun seekToNote(index: Int) { - try { - sequencer?.let { sequencer -> - if (sequencer.isOpen) { - val targetPos = index * MS8PER_NOTE * 1000 - sequencer.microsecondPosition = targetPos - } - } - } catch (e: Exception) { - e.printStackTrace() - } - } private fun saveVoicesVolumes() { val data = voiceVolumes.joinToString(",")