Sync grid number with marker & fix DC (simple) & 𝄐

This commit is contained in:
hasinarak3@gmail.com 2026-03-10 13:59:52 +03:00
parent c53e46b4b4
commit 12012d8e1d
4 changed files with 307 additions and 174 deletions

View file

@ -593,7 +593,34 @@ fun LazyVerticalGridTUO(
-1 -1
}*/ }*/
val measures = tuoList.drop(1).chunked(gridColumnCount) val measures = tuoList.drop(1).chunked(gridColumnCount)
val metadataList = mutableListOf<MidiMarkers>() // 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( Column(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
){ ){
@ -699,20 +726,6 @@ fun LazyVerticalGridTUO(
val myTimestamp = sharedScreenModel.tuoTimestamps.value.getOrElse(globalIndex) { 0L } 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) Box(modifier = Modifier.weight(1f)
.combinedClickable( .combinedClickable(
onClick = { onClick = {
@ -723,8 +736,9 @@ fun LazyVerticalGridTUO(
} , } ,
onDoubleClick = { onDoubleClick = {
val m = sharedScreenModel.getFullMarkers() val m = sharedScreenModel.getFullMarkers()
m.forEach { (timestamp, template, lastCallerMarker, marker, noteBefore, separat, note) -> m.forEach { (timestamp, gridIndex, template, lastCallerMarker, marker, noteBefore, separat, note) ->
println("Allmarker : $marker in $timestamp ms & note= $note & nBefore =$noteBefore & separateur = $separat") println("Allmarker $marker in $gridIndex on $timestamp ms")
// & note= $note & nBefore =$noteBefore & separateur = $separat
} }
} }
)) { )) {

View file

@ -2,6 +2,7 @@ package mg.dot.feufaro.viewmodel
data class MidiMarkers( data class MidiMarkers(
val timestamp: Long, val timestamp: Long,
val gridIndex: Int? = 0,
val template: String, val template: String,
val lastCallerMarker: Int, val lastCallerMarker: Int,
val marker: String, val marker: String,

View file

@ -6,7 +6,6 @@ import cafe.adriel.voyager.core.model.ScreenModel
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -143,9 +142,15 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
private val _midiMarkersList = MutableStateFlow<List<MidiMarkers>>(emptyList()) private val _midiMarkersList = MutableStateFlow<List<MidiMarkers>>(emptyList())
val midiMarkersList: StateFlow<List<MidiMarkers>> = _midiMarkersList val midiMarkersList: StateFlow<List<MidiMarkers>> = _midiMarkersList
private val _activeIndex = MutableStateFlow(-1)
val activeIndex: StateFlow<Int> = _activeIndex.asStateFlow()
fun updateMidiData(newList: List<MidiMarkers>) { fun updateActiveIndex(currentPosMs: Long) {
_midiMarkersList.value = newList val currentPosMicros = currentPosMs * 1000
val index = _tuoTimestamps.value.indexOfLast { it <= currentPosMicros }
if (_activeIndex.value != index) {
_activeIndex.value = index.coerceAtLeast(0)
}
} }
fun getFullMarkers(): List<MidiMarkers> { fun getFullMarkers(): List<MidiMarkers> {
@ -165,58 +170,90 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
return _dsDone.value return _dsDone.value
} }
fun finalizeMarkers() { fun getTotalGridCount(): Int {
val timestamps = _tuoTimestamps.value return _tuoList.value.drop(1).size - 1
}
fun updateAndFinalizeMidiData(rawList: List<MidiMarkers>) {
// val timestamps = _tuoTimestamps.value
val tuos = _tuoList.value.drop(1) val tuos = _tuoList.value.drop(1)
val newMtdList = mutableListOf<MidiMarkers>()
tuos.forEachIndexed { index, tuo -> val finalizedList = rawList.map { marker ->
val markerText = tuo.pTemplate.markerToString() var markerText = marker.marker
if (markerText.isNotEmpty()) { val index = marker.gridIndex ?: 0 // On utilise l'index passé par l'UI
val ts = timestamps.getOrNull(index) ?: 0L val isNearEnd = index >= (tuos.size - 2)
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 ?: ""
// On se position sur la note apres les tiret "-" ou sur / val isDC = markerText.contains(Regex("""D\.?C\.?"""))
if ((currentNote != "" && currentNote.isNotBlank()) || (currentSep == "/")) { val isDS = markerText.contains(Regex("""D\.?S\.?"""))
val currentTs = timestamps.getOrNull(internIn) ?: 0L
println("Dernier '-' passé. Nouvelle note '$currentNote' trouvée à l'index $internIn (Position du saut)") var resultMarker: MidiMarkers = marker
newMtdList.clear()
newMtdList.add( if (isDC) {
MidiMarkers( var forwardIndex = index
timestamp = currentTs, while (forwardIndex < tuos.size) {
template = currentTuo.pTemplate.template, val currentTuo = tuos.getOrNull(forwardIndex)
lastCallerMarker = currentTuo.pTemplate.lastCalledMarker, val currentNote = currentTuo?.tuNotes?.getOrNull(1)?.toString() ?: ""
marker = "DC_GROUP_PART", val currentSep = currentTuo?.sep0 ?: ""
noteBefore = currentTuo.prevTUO!!.pTemplate!!.template,
separat = currentTuo.sep0, // println("Je suis sur $forwardIndex note $currentNote Sep $currentSep \t condition: ${(currentNote == "―")} || ${(currentSep == "/")}")
note = currentNote // Tant que fin de mesure
) if (currentSep == "/") {
resultMarker = marker.copy(
gridIndex = forwardIndex-1,
marker = "${markerText.trim()}_GROUP_PART"
) )
break break
} }
internIn++ 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
}
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) { fun loadNewSong(newMidiFile: String) {
@ -244,7 +281,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
}) })
// synchro // synchro
_mediaPlayer?.requestSync(this) _mediaPlayer?.requestSync(this)
finalizeMarkers() //finalizeMarkers()
_mediaPlayer?.syncNavigationMonitor(this) _mediaPlayer?.syncNavigationMonitor(this)
} catch(e: Exception) { } catch(e: Exception) {
println("Erreur d'ouverture de mediaPlayer / ") println("Erreur d'ouverture de mediaPlayer / ")
@ -268,7 +305,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
player.seekTo(0) 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() { fun stopMidi() {

View file

@ -45,15 +45,16 @@ actual class FMediaPlayer actual constructor(
private var navigationJob: Job? = null private var navigationJob: Job? = null
private data class NavigationStep( private data class NavigationStep(
val triggerMs: Long, // DC | DS val marker: String,
val targetMs: Long, // farany, $ ,.. val gridIndex: Int, // L'ancre absolue
val targetGrid: Int = 0,
val isHold: Boolean = false, // point d'orgue val isHold: Boolean = false, // point d'orgue
var alreadyDone: Boolean = false, // done var alreadyDone: Boolean = false, // done
var beatInDC: Int = 1, // temps note sur le marker var beatInDC: Int = 1, // temps note sur le marker
var sustainNedd: Boolean = true
) )
private val navigationSteps = mutableListOf<NavigationStep>() private val navigationSteps = mutableListOf<NavigationStep>()
private var boundModel: SharedScreenModel? = null
init { init {
try { try {
sequencer?.open() sequencer?.open()
@ -69,6 +70,8 @@ actual class FMediaPlayer actual constructor(
if (file.exists()) { if (file.exists()) {
val mySequence = MidiSystem.getSequence(file) val mySequence = MidiSystem.getSequence(file)
sequencer?.sequence = mySequence sequencer?.sequence = mySequence
stripTempoEvents(mySequence)
sequencer?.tempoFactor = 1.0f
loadVoiceVolumes() loadVoiceVolumes()
applyVoiceStates() applyVoiceStates()
sequencer?.addMetaEventListener { meta -> sequencer?.addMetaEventListener { meta ->
@ -76,6 +79,15 @@ actual class FMediaPlayer actual constructor(
onFinished() 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 { } else {
// Créeons une fichier vide au 1er lancement de l'application, après MidiWriterKotlin l'écrasera // Créeons une fichier vide au 1er lancement de l'application, après MidiWriterKotlin l'écrasera
val f0file = File("${getConfigDirectoryPath()}whawyd3.mid") 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<javax.sound.midi.MidiEvent>()
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() { private fun resetNavigationFlags() {
navigationSteps.forEach { it.alreadyDone = false } navigationSteps.forEach { it.alreadyDone = false }
} }
private fun prepareNavigation(sharedScreenModel: SharedScreenModel) { private fun prepareNavigation(sharedScreenModel: SharedScreenModel) {
val metadataList = sharedScreenModel.getFullMarkers() val metadataList = sharedScreenModel.getFullMarkers()
navigationSteps.clear() navigationSteps.clear()
var holdPos: Long = 0L var lastSegno = 0
val currentBpm = sequencer?.tempoInBPM ?: 120f metadataList.forEach { (timestamp, gridIndex, template, lastCallerMarker, marker, noteBefore, separat, note) ->
val beatDurationMs = (60_000 / currentBpm).toLong() val currentIndex = gridIndex ?: 0
metadataList.forEach { (timestamp, template, lastCallerMarker, marker, noteBefore, separat, note) -> val dsRegex = Regex("""D\.?S\.?""")
val timeMs = (timestamp / 1000) 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 { when {
marker.contains("$") -> {
lastSegno = currentIndex
println("Cible ($) mémorisée au $lastSegno")
}
marker.contains("\uD834\uDD10") -> { marker.contains("\uD834\uDD10") -> {
holdPos = timeMs
val beat = if(note.contains('•')) 2 else 1 // demi-ton sur .) ou non val beat = if(note.contains('•')) 2 else 1 // demi-ton sur .) ou non
navigationSteps.add(FMediaPlayer.NavigationStep(timeMs, -1L, isHold = true, beatInDC = beat)) navigationSteps.add(
println("Point d'orgue (\uD834\uDD10) mémorisée à $holdPos ms") FMediaPlayer.NavigationStep(
marker,
currentIndex,
isHold = true,
beatInDC = beat
)
)
println("Point d'orgue (\uD834\uDD10) mémorisée au grille n° $gridIndex")
} }
// Déclencheurs DC /*// DS
marker == "DC_GROUP_PART" -> { dsGPattern.matches(marker.trim())-> {
var needSustaine = false val target = if (lastSegno > 0) lastSegno else 0
var trigger = timeMs - (beatDurationMs / 2) 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)) navigationSteps.add(
println("Lien créé : $marker à $trigger ms vers cible 0 ms Avec Sustaine? $needSustaine") 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 '-'*/ (dcRegex.matches(marker.trim()) && !marker.contains("DC_GROUP_PART")) -> {
marker == "DC" -> { println("dernier grille $last_grid")
var needSustaine = false var indx = if((last_grid - currentIndex) <= 0) {
val trigger = timeMs currentIndex /*- 1*/
// Si le marker si place près de la fin de la partition } else currentIndex
if((getDuration() - timeMs) <= beatDurationMs) { navigationSteps.add(
println("Fin très proche") NavigationStep(
needSustaine = true marker,
} indx,
navigationSteps.add(NavigationStep(trigger, targetMs = 0, sustainNedd = needSustaine)) targetGrid = 0
println("Lien créé : $marker à $trigger ms vers le début") )
)
println("Lien DC créé : $marker à $indx vers le début")
} }
} }
} }
@ -130,90 +212,101 @@ actual class FMediaPlayer actual constructor(
var dcDone = sharedScreenModel.getDcDone() var dcDone = sharedScreenModel.getDcDone()
val dsDone = sharedScreenModel.getDsDone() 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?.cancel()
navigationJob = playerScope.launch { navigationJob = playerScope.launch(Dispatchers.Default) {
while (isActive) { sharedScreenModel.activeIndex.collect { currentIndex ->
if (sequencer?.isRunning == true) {
val currentPos = getCurrentPosition() val availableIndices = navigationSteps.map { it.gridIndex }
val duration = getDuration() println("bpm:$targetBpm _ ${sequencer?.tempoInBPM}")
val measure = sharedScreenModel.measure.value println("MONITOR : Reçu Index $currentIndex | Index en mémoire : $availableIndices ")
val firstVal = measure.substringBefore("/")
val mesureNum = firstVal.toIntOrNull() ?: 4 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 { val step = navigationSteps.find {
currentPos >= it.triggerMs && it.gridIndex == currentIndex &&
currentPos < it.triggerMs + 800 &&
!it.alreadyDone !it.alreadyDone
} }
if (step != null) { if (step != null) {
if (currentPos < step.triggerMs + 800) { // Point d'orgue
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) { 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) } synthetizer?.channels?.forEach { it?.controlChange(64, 127) }
val previousFactor = sequencer?.tempoFactor ?: 1.0f
sequencer?.tempoFactor = 0.0001f sequencer?.tempoFactor = 0.0001f
if(step.beatInDC == 1){ delay(holdDuration)
delay(beatDurationMs * 2)
} else { //démi-ton sur le point d'orgue sequencer?.tempoFactor = previousFactor
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) } synthetizer?.channels?.forEach { it?.controlChange(64, 0) }
sequencer?.tempoFactor = currentTempo
step.alreadyDone=false
} else { } else {
// $ dc ds ...
step.alreadyDone = true step.alreadyDone = true
// Sustain val currentBpm = targetBpm
if(step.sustainNedd) { val beatDuration = (60_000 / currentBpm).toLong()
println("avant de sauter bpm=$targetBpm")
synthetizer?.channels?.forEach { it?.controlChange(64, 127) } synthetizer?.channels?.forEach { it?.controlChange(64, 127) }
sequencer?.tempoFactor = 0.0001f sequencer?.tempoFactor = 0.0001f
delay(beatDuration)
delay(beatDurationMs * (mesureNum/2)) // 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")
}
}
}
}
}
}
private var syncJob: Job? = null
synthetizer?.channels?.forEach { private fun startSyncLoop(sharedScreenModel: SharedScreenModel) {
it?.controlChange(64, 0) syncJob?.cancel()
it?.controlChange(123, 0) 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
delay(50) // Silence de respiration val bpm = sequencer?.tempoInBPM ?: targetBpm
val usPerTick = (60_000_000.0 / bpm) / resolution
val targetMicros = (tick * usPerTick).toLong()
sequencer?.tempoFactor = currentTempo sequencer?.microsecondPosition = targetMicros
setVolume(currentGlobalVolume)
}
seekTo(step.targetMs)
if (sequencer?.isRunning == false) sequencer?.start()
// delay(500)
}
}
delay(300)
}
}
delay(100)
}
}
} }
actual fun play(){ actual fun play(){
if (!sequencer!!.isOpen){ if (sequencer?.isOpen == true){
sequencer!!.open() applyBpm()
}
sequencer?.start() sequencer?.start()
println("La sequence vient d etre lancé ${sequencer?.isRunning}") println("La sequence vient d etre lancé ${sequencer?.isRunning}")
// sequencer!!.open()
}
} }
actual fun pause(){ actual fun pause(){
sequencer?.stop() sequencer?.stop()
@ -226,14 +319,16 @@ actual class FMediaPlayer actual constructor(
} }
actual fun syncNavigationMonitor(sharedScreenModel: SharedScreenModel) { actual fun syncNavigationMonitor(sharedScreenModel: SharedScreenModel) {
this.boundModel = sharedScreenModel
prepareNavigation(sharedScreenModel) prepareNavigation(sharedScreenModel)
startNavigationMonitor(sharedScreenModel) startNavigationMonitor(sharedScreenModel)
startSyncLoop(sharedScreenModel)
} }
fun syncTuoWithMidi(sequence: Sequence, sharedScreenModel: SharedScreenModel) { fun syncTuoWithMidi(sequence: Sequence, sharedScreenModel: SharedScreenModel) {
val timestamps = mutableListOf<Long>() val timestamps = mutableListOf<Long>()
val resolution = sequence.resolution.toDouble() 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 val usPerTick = (60_000_000.0 / bpm) / resolution
// détecter par 60 ticks càd par un temp (noir) // 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) { actual fun seekTo(position: Long) {
sequencer?.microsecondPosition = position * 1000 sequencer?.microsecondPosition = position * 1000
applyBpm()
} }
actual fun release() { actual fun release() {
sequencer?.close() sequencer?.close()
@ -316,7 +412,7 @@ actual class FMediaPlayer actual constructor(
} catch (e: Exception){ } catch (e: Exception){
e.printStackTrace() e.printStackTrace()
} }
println("la volume $level") // println("la volume $level")
} }
actual fun setPointA() { actual fun setPointA() {
@ -376,7 +472,7 @@ actual class FMediaPlayer actual constructor(
channels[i].controlChange(123, 0) channels[i].controlChange(123, 0)
} }
} }
println("SATB $i Volumes: ${voiceVolumes[i]}") // println("SATB $i Volumes: ${voiceVolumes[i]}")
} }
} }
} catch (e: Exception) { e.printStackTrace() } } catch (e: Exception) { e.printStackTrace() }
@ -405,21 +501,6 @@ actual class FMediaPlayer actual constructor(
} }
println("Tempo réglé à : $bpm BPM") 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() { private fun saveVoicesVolumes() {
val data = voiceVolumes.joinToString(",") val data = voiceVolumes.joinToString(",")