Add Modify silent duration on first index partition & fix bug on multiple includ sources ...

This commit is contained in:
Hasinjato 2026-05-29 13:03:30 +03:00
parent 1f05027f1f
commit 6b8ce0cecf
4 changed files with 247 additions and 55 deletions

View file

@ -384,13 +384,27 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
// println("fullLine = '$fullLine'") // println("fullLine = '$fullLine'")
val afterU0 = fullLine.substringAfter("U0:") val afterU0 = fullLine.substringAfter("U0:")
val blankPrefixRegex = Regex("^(.*?z[0-9A-Z]:)") val blankPrefixRegex = Regex("^(.*?z)([0-9A-Z_+\\-]+)(:)")
val match = blankPrefixRegex.find(afterU0) val match = blankPrefixRegex.find(afterU0)
val blankPrefix = match?.value ?: "" var blankPrefix: String = ""
val body = if (match != null) { var body: String = ""
afterU0.substring(blankPrefix.length) if (match != null) {
body = afterU0.substring(blankPrefix.length)
if (editState.silentDuration == "-1") {
blankPrefix = match.value
}
else {
println("voici ${match.groupValues.joinToString(",")}")
val beforeDuration = match.groups[1]?.value ?: ""
val restOfSilenceBlock = match.groups[3]?.value ?: ":"
blankPrefix = "$beforeDuration${editState.silentDuration}$restOfSilenceBlock"
}
body = afterU0.substring(match.value.length)
} else { } else {
afterU0 blankPrefix = afterU0.substringBefore(":") + ":"
body = afterU0.substringAfter(":")
} }
val measure = measureString.split("/")[0].toIntOrNull() ?: 4 val measure = measureString.split("/")[0].toIntOrNull() ?: 4
@ -506,29 +520,26 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
val fullPath = directory + fileName val fullPath = directory + fileName
val rawIncludedContent = fileRepository.readFileLines(fullPath) val rawIncludedContent = fileRepository.readFileLines(fullPath)
if (parts.size > 2 && parts[2].startsWith("^")) { if (parts.size > 2 && parts[2].isNotEmpty()) {
val patternInside = parts[2] val regexIgnoreString = parts[2]
.substringAfter("(", "")
.substringBefore(")", "")
val prefixesToIgnore = if (patternInside.isNotEmpty()) { val ignoredLinesRegex = try {
patternInside.split("|") Regex(regexIgnoreString)
} else { } catch (e: Exception) {
listOf(parts[2].substring(1)) Regex("^:")
} }
val filteredLines = rawIncludedContent.filter { incLine -> val filteredLines = rawIncludedContent.filter { incLine ->
prefixesToIgnore.none { prefix -> ignoredLinesRegex.find(incLine) == null
incLine.trim().startsWith(prefix)
}
} }
lineBuilder.append(filteredLines.joinToString("\n")) lineBuilder.append(filteredLines.joinToString("\n"))
} else { } else {
lineBuilder.append(rawIncludedContent.joinToString("\n")) lineBuilder.append(rawIncludedContent.joinToString("\n"))
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
lineBuilder.append("// Erreur: ${e.message}") lineBuilder.append("// Erreur inclusion: ${e.message}")
} }
lastIndex = match.range.last + 1 lastIndex = match.range.last + 1
} }
@ -768,6 +779,12 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
newMarkerValue: String, newMarkerValue: String,
editState: TUOEditState editState: TUOEditState
): String { ): String {
/* On ne modifie pas le template tant que les notes ne sont pas modifiées
* Et tant qu'une marker est ajouter */
if (newMarkerValue.isEmpty()) {
println("Aucune modification détectée (marqueur vide), on retourne le template original.")
return templateLine
}
val isT0 = templateLine.startsWith("T0") val isT0 = templateLine.startsWith("T0")
val prefix: String val prefix: String
@ -1291,6 +1308,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
var lookK = k var lookK = k
var lookC = charIdx + 1 var lookC = charIdx + 1
var searching = true var searching = true
var crossedParenthesis = false
while (searching) { while (searching) {
if (lookC >= buff[lookK].length) { if (lookC >= buff[lookK].length) {
@ -1317,20 +1335,28 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
lookC++ lookC++
} }
next == '(' || next == ')' -> {
crossedParenthesis = true
lookC++
}
next == 'z' && lookK == k && char == 'D' -> { next == 'z' && lookK == k && char == 'D' -> {
var hasFutureResonance = false if (crossedParenthesis) {
if (k + 1 < buff.size) {
if (buff[k + 1].replace(Regex("\\$\\{.*?\\}|\\$."), "")
.startsWith("-")
) hasFutureResonance = true
}
if (!hasFutureResonance && currentStr.contains(".") && currentStr.contains(",")) {
val resDur = getDurationAt(currentStr, lookC, k)
totalDuration += resDur
consumedGlobally.add(lookK to lookC)
lookC++
} else {
searching = false searching = false
} else {
var hasFutureResonance = false
if (k + 1 < buff.size) {
if (buff[k + 1].replace(Regex("\\$\\{.*?\\}|\\$."), "")
.startsWith("-")
) hasFutureResonance = true
}
if (!hasFutureResonance && currentStr.contains(".") && currentStr.contains(",")) {
val resDur = getDurationAt(currentStr, lookC, k)
totalDuration += resDur
consumedGlobally.add(lookK to lookC)
lookC++
} else {
searching = false
}
} }
} }
@ -1411,6 +1437,15 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
val cleanedBlocks = buff2.map { it.replace(" ", "") } val cleanedBlocks = buff2.map { it.replace(" ", "") }
/*val durationModifierRegex = Regex("^(.*?z)[0-9A-Z_+\\-](:|/)")
val match = durationModifierRegex.find(prefix)
val newPrefix = if (match != null) {
val beforeDuration = match.groups[1]?.value ?: ""
val separator = match.groups[2]?.value ?: ":"
"$beforeDuration${editState.silentDuration}$separator"
} else prefix*/
val finalLines = (prefix + cleanedBlocks.joinToString("")) val finalLines = (prefix + cleanedBlocks.joinToString(""))
.replace("2z11", "y") .replace("2z11", "y")
.replace("/", "/ ") .replace("/", "/ ")

View file

@ -14,6 +14,8 @@ import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.KeyboardDoubleArrowLeft
import androidx.compose.material.icons.filled.KeyboardDoubleArrowRight
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -63,6 +65,7 @@ fun TUODetailDialog(
var templateFragment by remember { mutableStateOf(editState.templateFragment) } var templateFragment by remember { mutableStateOf(editState.templateFragment) }
var marker by remember { mutableStateOf(editState.marker) } var marker by remember { mutableStateOf(editState.marker) }
val initialMarker = remember { editState.marker }
LaunchedEffect(notes.toMap()) { LaunchedEffect(notes.toMap()) {
val tempState = TUOEditState( val tempState = TUOEditState(
@ -116,6 +119,79 @@ fun TUODetailDialog(
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
// Modif silence duration
if (currentIndex == 0) {
val silentDurationOrder = listOf(
"0", "4", "8", "A", "C", "E", "G", "K", "O", "S", "W"
)
Row(
modifier = Modifier.width(125.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
IconButton(
onClick = {
val currentIdx = silentDurationOrder.indexOf(editState.silentDuration)
val newIdx = if (currentIdx > 0) currentIdx - 1 else 0
val newDuration = silentDurationOrder[newIdx]
val state = TUOEditState(
tuoIndex = globalIndex,
notesByVoice = notes.toMap(),
originalNotes = originalNotes.toMap(),
originalLyricsByStanza = originalLyricsByStz.toMutableMap(),
lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")),
templateFragment = templateFragment,
marker = if(marker != initialMarker) marker else "",
silentDuration = newDuration
)
onSave(state)
},
enabled = silentDurationOrder.indexOf(editState.silentDuration) >= 0,
modifier = Modifier.size(20.dp)
) {
Icon(
imageVector = Icons.Default.KeyboardDoubleArrowLeft,
contentDescription = "Diminuer",
tint = if (silentDurationOrder.indexOf(editState.silentDuration) > 0) Color.LightGray else Color.DarkGray
)
}
Text(
text = editState.silentDuration,
color = Color.White,
style = MaterialTheme.typography.bodyLarge
)
IconButton(
onClick = {
val currentIdx = silentDurationOrder.indexOf(editState.silentDuration)
val newIdx = if (currentIdx != -1 && currentIdx < silentDurationOrder.lastIndex) currentIdx + 1 else currentIdx
val newDuration = silentDurationOrder[newIdx]
val state = TUOEditState(
tuoIndex = globalIndex,
notesByVoice = notes.toMap(),
originalNotes = originalNotes.toMap(),
originalLyricsByStanza = originalLyricsByStz.toMutableMap(),
lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")),
templateFragment = templateFragment,
marker = if(marker != initialMarker) marker else "",
silentDuration = newDuration
)
onSave(state)
},
enabled = silentDurationOrder.indexOf(editState.silentDuration) < silentDurationOrder.lastIndex,
modifier = Modifier.size(20.dp)
) {
Icon(
imageVector = Icons.Default.KeyboardDoubleArrowRight,
contentDescription = "Augmenter",
tint = if (silentDurationOrder.indexOf(editState.silentDuration) < silentDurationOrder.lastIndex) Color.LightGray else Color.DarkGray
)
}
}
}
if (currentIndex > 0) { if (currentIndex > 0) {
IconButton( IconButton(
onClick = { onClick = {
@ -130,14 +206,14 @@ fun TUODetailDialog(
tint = Color.White tint = Color.White
) )
} }
}
Text( Text(
text = "$currentIndex", text = "$currentIndex",
fontSize = 15.sp, fontSize = 15.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = Color.White color = Color.White
) )
}
IconButton( IconButton(
onClick = { onClick = {
@ -288,7 +364,9 @@ fun TUODetailDialog(
if (isEditable || canAdd) { if (isEditable || canAdd) {
IconButton( IconButton(
onClick = { onClick = {
if (index == 0) lyricsLines.add("_") else lyricsLines.removeAt(index) if (index >= 0) {
if (index == 0) lyricsLines.add("_") else lyricsLines.removeAt(index)
}
}) { }) {
Icon( Icon(
imageVector = if (index == 0) Icons.Default.Add else Icons.Default.Clear, imageVector = if (index == 0) Icons.Default.Add else Icons.Default.Clear,
@ -323,7 +401,8 @@ fun TUODetailDialog(
originalLyricsByStanza = originalLyricsByStz.toMutableMap(), originalLyricsByStanza = originalLyricsByStz.toMutableMap(),
lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")), lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")),
templateFragment = templateFragment, templateFragment = templateFragment,
marker = marker /* On ne ré-insere pas la même marker*/
marker = if(marker != initialMarker) marker else "",
) )
onSave(state) onSave(state)
}, },
@ -567,7 +646,7 @@ private fun notesToTemplate(notes: String): String {
.replace("si", "S") .replace("si", "S")
.replace("ta", "T") .replace("ta", "T")
val noteRegex = Regex("""([drmfsltDRFSTz]+|[.,()\s-])""") val noteRegex = Regex("""([drmfsltDRFSTz][ia]?|[.,()\s-])""")
val template = StringBuilder() val template = StringBuilder()
val matches = noteRegex.findAll(cleaned) val matches = noteRegex.findAll(cleaned)

View file

@ -7,5 +7,6 @@ data class TUOEditState(
val lyricsByStanza: MutableMap<Int, String> = mutableMapOf(), val lyricsByStanza: MutableMap<Int, String> = mutableMapOf(),
val originalLyricsByStanza: MutableMap<Int, String> = mutableMapOf(), val originalLyricsByStanza: MutableMap<Int, String> = mutableMapOf(),
val templateFragment: String = "", val templateFragment: String = "",
val marker: String = "" val marker: String = "",
val silentDuration: String = "-1"
) )

View file

@ -16,9 +16,11 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Undo import androidx.compose.material.icons.automirrored.filled.Undo
import androidx.compose.material.icons.filled.ArrowBackIosNew
import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Build
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Description import androidx.compose.material.icons.filled.Description
import androidx.compose.material.icons.filled.KeyboardDoubleArrowLeft
import androidx.compose.material.icons.filled.Save import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
@ -274,7 +276,7 @@ fun TimeUnitComposable(
) )
Column( Column(
modifier = Modifier modifier = Modifier
.background(/*if(gridActive) Color.Cyan.copy(alpha = 0.5f) else col*/animatedColor) .background(animatedColor)
) { ) {
if (TimeUnitObject._hasMarker) { if (TimeUnitObject._hasMarker) {
val lineHeight = 20.sp val lineHeight = 20.sp
@ -841,6 +843,25 @@ fun LazyVerticalGridTUO(
val template = oneTUO.pTemplate.template val template = oneTUO.pTemplate.template
val expectedTemplate = template.count { it.isLetter() } == 2 && template.contains(".") val expectedTemplate = template.count { it.isLetter() } == 2 && template.contains(".")
val fileC = sharedScreenModel.fileContent.value ?: ""
val lines = fileC.split("\n").toMutableList()
val u0Idx = lines.indexOfFirst { it.startsWith("U0:") }
val t0Idx = lines.indexOfFirst { it.startsWith("T0:") }
val fullLine = when {
t0Idx != -1 -> {
lines[t0Idx]
}
u0Idx != -1 -> {
lines[u0Idx]
}
else -> ""
}
val afterU0T0 = fullLine.substringAfter("0:")
val blankPrefixRegex =Regex("""z([0-9A-Z])[:]""")
val match = blankPrefixRegex.find(afterU0T0)
val blankPrefix = match?.groups?.get(1)?.value ?: ""
val editState = TUOEditState( val editState = TUOEditState(
tuoIndex = oneTUO.firstTuoIndex, tuoIndex = oneTUO.firstTuoIndex,
notesByVoice = (0..3).associate { i -> notesByVoice = (0..3).associate { i ->
@ -855,7 +876,8 @@ fun LazyVerticalGridTUO(
marker = listOfNotNull( marker = listOfNotNull(
oneTUO.pTemplate.markerToString().takeIf { it.isNotBlank() }, oneTUO.pTemplate.markerToString().takeIf { it.isNotBlank() },
oneTUO.hasHairPin()?.toString()?.takeIf { it.isNotBlank() } oneTUO.hasHairPin()?.toString()?.takeIf { it.isNotBlank() }
).joinToString(" ") ).joinToString(" "),
silentDuration = blankPrefix
) )
TUODetailDialog( TUODetailDialog(
editState = editState, editState = editState,
@ -1274,12 +1296,16 @@ fun EditorActionButtons(isUndoVisible: Boolean, onUndo: () -> Unit, onBuild: ()
} }
fun autoFixNote(rawNote: String, template: String): String { fun autoFixNote(rawNote: String, template: String): String {
println("ça on a t $template => $rawNote") // println("\n==================================================")
// println(" ENTRÉE autoFixNote")
// println(" • Template : \"$template\"")
// println(" • RawNote : \"$rawNote\"")
// println("==================================================")
if (rawNote == "_") return rawNote if (rawNote == "_") return rawNote
val noteRegex = Regex("([drmfsltDRFSTw][ia]?|―)[0-9'¹²³⁴₁₂₃₄,]*") val noteRegex = Regex("([drmfsltDRFSTw][ia]?|―)[0-9'¹²³⁴₁₂₃₄,]*")
val actualNotes = if (rawNote.isEmpty()) emptyList() else noteRegex.findAll(rawNote).map { it.value }.toList() val actualNotes = if (rawNote.isEmpty()) emptyList() else noteRegex.findAll(rawNote).map { it.value }.toList()
// println(" • Notes détectées dans RawNote : $actualNotes (Taille: ${actualNotes.size})")
/*if (rawNote.contains("") || rawNote.contains("") || rawNote.contains(",")) { /*if (rawNote.contains("") || rawNote.contains("") || rawNote.contains(",")) {
val templateNotesCount = template.count { c -> val templateNotesCount = template.count { c ->
c in 'A'..'Y' || c in 'a'..'y' c in 'A'..'Y' || c in 'a'..'y'
@ -1289,24 +1315,32 @@ fun autoFixNote(rawNote: String, template: String): String {
val requiredNotesCount = template.count { it in 'A'..'Y' || it in 'a'..'y' } val requiredNotesCount = template.count { it in 'A'..'Y' || it in 'a'..'y' }
val shouldIncludeParentheses = actualNotes.size >= requiredNotesCount val shouldIncludeParentheses = actualNotes.size >= requiredNotesCount
// println(" • Notes requises par le template : $requiredNotesCount")
// println(" • Inclusion des parenthèses autorisée ? : $shouldIncludeParentheses")
// println("--------------------------------------------------")
val result = StringBuilder() val result = StringBuilder()
var noteIdx = 0 var noteIdx = 0
for (i in template.indices) { for (i in template.indices) {
val char = template[i] val char = template[i]
// print("[Étape $i] Caractère template = '$char' | Index note actuel = $noteIdx -> ")
when { when {
(char in 'A'..'Y' || char in 'a'..'y') -> { (char in 'A'..'Y' || char in 'a'..'y') -> {
if (noteIdx < actualNotes.size) { if (noteIdx < actualNotes.size) {
val noteToAdd = actualNotes[noteIdx] val noteToAdd = actualNotes[noteIdx]
if (noteIdx > 0 && result.isNotEmpty()) { if (noteIdx > 0 && result.isNotEmpty()) {
val lastChar = result.last() val lastChar = result.last()
if (lastChar != '•' && lastChar != ',' && lastChar != '(' && lastChar != ' ') { // println()
// println(" [INFO ESPACE] lastC '$lastChar' avant d'ajouter la note \"$noteToAdd\"")
/* if (lastChar != '•' && lastChar != ',' && lastChar != '(' && lastChar != ')') {
result.append(" ") result.append(" ")
} } */
// println(" [INFO ESPACE] Maintenant res est \"${result.toString()}\"")
// print(" -> Continuité Étape $i : ")
} }
result.append(noteToAdd) result.append(noteToAdd)
// println("Action : Ajout de la note utilisateur \"$noteToAdd\"")
noteIdx++ noteIdx++
} else { } else {
/*if (rawNote.isEmpty()) {*/ /*if (rawNote.isEmpty()) {*/
@ -1314,35 +1348,78 @@ fun autoFixNote(rawNote: String, template: String): String {
/*} else { /*} else {
result.append("") result.append("")
}*/ }*/
// println("Action : Plus de notes utilisateur ! Ajout du silence par défaut \"z\"")
} }
} }
char == '-' -> { char == '-' -> {
if (noteIdx < actualNotes.size && actualNotes[noteIdx] == "") { if (noteIdx < actualNotes.size && actualNotes[noteIdx] == "") {
result.append("") result.append("")
// println("Action : Consommation et ajout du tiret utilisateur \"―\"")
noteIdx++ noteIdx++
} else { } else {
val prev = if (i > 0) template[i - 1] else null val prev = if (i > 0) template[i - 1] else null
val next = if (i < template.lastIndex) template[i + 1] else null val next = if (i < template.lastIndex) template[i + 1] else null
if (prev == '.' && next == ',') result.append(" ") else result.append("") if (prev == '.' && next == ',') {
result.append(" ")
// println("Action : Structure .-, détectée -> Ajout d'un espace \" \"")
} else {
result.append("")
// println("Action : Ajout du tiret automatique \"―\"")
}
} }
} }
char == 'z' -> result.append(" ") char == 'z' -> {
result.append(" ")
// println("Action : Caractère 'z' du template -> Ajout d'un espace \" \"")
}
char == '.' -> { char == '.' -> {
val currentText = result.toString() val currentText = result.toString()
if (!currentText.endsWith("")) result.append("") if (!currentText.endsWith("")) {
result.append("")
// println("Action : Ajout du point musical \"•\"")
} else {
// println("Action : Ignoré (se termine déjà par un point)")
}
} }
char == ',' -> { char == ',' -> {
val currentText = result.toString() val currentText = result.toString()
if (!currentText.endsWith(",")) result.append(",") if (!currentText.endsWith(",")) {
result.append(",")
// println("Action : Ajout de la virgule \",\"")
} else {
// println("Action : Ignoré (se termine déjà par une virgule)")
}
} }
char == '(' -> { char == '(' -> {
if (shouldIncludeParentheses) result.append("(") if (shouldIncludeParentheses) {
result.append("(")
// println("Action : Ajout parenthèse ouvrante \"(\"")
} else {
// println("Action : Ignoré (pas assez de notes)")
}
} }
char == ')' -> { char == ')' -> {
if (shouldIncludeParentheses) result.append(")") if (shouldIncludeParentheses) {
result.append(")")
// println("Action : Ajout parenthèse fermante \")\"")
} else {
// println("Action : Ignoré (pas assez de notes)")
}
}
else -> {
// println("Action : Aucun comportement défini pour '$char'")
} }
} }
// println(" ↳ État actuel du buffer : \"${result.toString()}\"")
} }
// println("on a RES ${result.toString()}") var finalResult = result.toString()
return result.toString() // println(" • Avant formatage final : \"$finalResult\"")
finalResult = finalResult
.replace("( ", "(")
.replace(" )", ")")
.replace("•,", "• ,")
// println("on a RES ${finalResult}")
return finalResult
} }