From 08c6fd25c47af44404dfd051d5257757a5d7c2fb Mon Sep 17 00:00:00 2001 From: Hasinjato Date: Wed, 16 Sep 2026 11:50:11 +0300 Subject: [PATCH] Isolate nuances marker --- .../kotlin/mg/dot/feufaro/solfa/PTemplate.kt | 136 ++++++++++-------- .../mg/dot/feufaro/solfa/TimeUnitObject.kt | 11 +- .../feufaro/viewmodel/SharedScreenModel.kt | 58 +------- 3 files changed, 84 insertions(+), 121 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/PTemplate.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/PTemplate.kt index accb391..2daa9e9 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/PTemplate.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/PTemplate.kt @@ -37,68 +37,9 @@ class PTemplate (val template: String, val separatorAfter: String, private val m } } fun markerToList(): List { - if (markers.isEmpty()) { - return emptyList() - } - val mString = markerToString() - return parseMarkers(mString) + if (markers.isEmpty()) return emptyList() + return parseMarkers(markerToString()) } - fun parseMarkers(rawText: String): List { - val tokens = mutableListOf() - if (rawText.isBlank()) return tokens - - var remaining = rawText.replace(",", " ") - .replace(Regex("""\b(DC)\b""", RegexOption.IGNORE_CASE), "D.C.") - .replace(Regex("""\b(DS)\b""", RegexOption.IGNORE_CASE), "D.S.") - .replace(Regex("""(D\.?C\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.C.Fin") - .replace(Regex("""(D\.?S\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.S.Fin") - .replace(Regex("""(D\.?C\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.C.alCoda") - .replace(Regex("""(D\.?S\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.S.alCoda") - - val allStaticMarkers = getAllMarker() - val tempoRegex = Regex("""^(♩\s*=\s*\d+)""", RegexOption.IGNORE_CASE) - val modulaRegex = Regex("""^(D(?:ô|o)?\s*dia\s*(?:C|Db|D|Eb|E|F|Gb|G|Ab|A|Bb|B))""", RegexOption.IGNORE_CASE) - - // Lexer - while (remaining.isNotEmpty()) { - remaining = remaining.trimStart() - if (remaining.isEmpty()) break - - val tempoMatch = tempoRegex.find(remaining) - if (tempoMatch != null) { - tokens.add(tempoMatch.value) - remaining = remaining.substring(tempoMatch.value.length) - continue - } - - val modulaMatch = modulaRegex.find(remaining) - if (modulaMatch != null) { - tokens.add(modulaMatch.value) - remaining = remaining.substring(modulaMatch.value.length) - continue - } - - val matchedMarker = allStaticMarkers - .sortedByDescending { it.length } - .find { marker -> - remaining.startsWith(marker, ignoreCase = true) && - (remaining.length == marker.length || - !remaining[marker.length].isLetter()) - } - - - if (matchedMarker != null) { - tokens.add(matchedMarker) - remaining = remaining.substring(matchedMarker.length) - } else { - val unknownChunk = remaining.takeWhile { !it.isWhitespace() } - tokens.add(unknownChunk) - remaining = remaining.substring(unknownChunk.length) - } - } - return tokens - } - fun hasKeyChange(): String { var returnFun = "" markers.map { @@ -111,4 +52,77 @@ class PTemplate (val template: String, val separatorAfter: String, private val m return returnFun } +} + +fun parseMarkers(rawText: String): List { + val tokens = mutableListOf() + if (rawText.isBlank()) return tokens + + var remaining = rawText.trim() + .replace(",", " ") + .replace(Regex("""\b(DC)\b""", RegexOption.IGNORE_CASE), "D.C.") + .replace(Regex("""\b(DS)\b""", RegexOption.IGNORE_CASE), "D.S.") + .replace(Regex("""(D\.?C\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.C.Fin") + .replace(Regex("""(D\.?S\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.S.Fin") + .replace(Regex("""(D\.?C\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.C.alCoda") + .replace(Regex("""(D\.?S\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.S.alCoda") + + val allStaticMarkers = getAllMarker().sortedByDescending { it.length } + val tempoRegex = Regex("""^(♩\s*=\s*\d+)""", RegexOption.IGNORE_CASE) + val modulaRegex = Regex("""^(D(?:ô|o)?\s*dia\s*(?:C|Db|D|Eb|E|F|Gb|G|Ab|A|Bb|B))""", RegexOption.IGNORE_CASE) + val measureRegex = Regex("""^\d+\s*/\s*\d+""") + + while (remaining.isNotEmpty()) { + remaining = remaining.trimStart() + if (remaining.isEmpty()) break + + val tempoMatch = tempoRegex.find(remaining) + if (tempoMatch != null) { + tokens.add(tempoMatch.value) + remaining = remaining.substring(tempoMatch.value.length) + continue + } + + val modulaMatch = modulaRegex.find(remaining) + if (modulaMatch != null) { + tokens.add(modulaMatch.value) + remaining = remaining.substring(modulaMatch.value.length) + continue + } + + val measureMatch = measureRegex.find(remaining) + if (measureMatch != null) { + tokens.add(measureMatch.value) + remaining = remaining.substring(measureMatch.value.length) + continue + } + + var matchedMarker: String? = null + for (marker in allStaticMarkers) { + if (remaining.startsWith(marker, ignoreCase = true)) { + val nextCharIndex = marker.length + val endsWithWordChar = marker.last().isLetterOrDigit() + + if (endsWithWordChar && nextCharIndex < remaining.length) { + val nextChar = remaining[nextCharIndex] + if (nextChar.isLetterOrDigit() || nextChar == ':' || nextChar == '=') { + continue + } + } + matchedMarker = marker + break + } + } + + if (matchedMarker != null) { + tokens.add(matchedMarker) + remaining = remaining.substring(matchedMarker.length) + } else { + val unknownChunk = remaining.takeWhile { !it.isWhitespace() } + tokens.add(unknownChunk) + remaining = remaining.substring(unknownChunk.length) + } + } + + return tokens.distinct() } \ No newline at end of file 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 a1164a3..0f56ab1 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt @@ -272,7 +272,12 @@ class TimeUnitObject (val pTemplate: PTemplate, val prevTUO: TimeUnitObject?, co private val REGEX_CLEAN_PREFIX = Regex("(\\d+\\.)+") private val musicForPtSerifBIRegex = Regex("""^(Largh\.|Grave|Largo|Lento|Adagio|And\.|Andantino|Mod\.|Moderato|Alleg\.|All\.|Viv\.|Vivacissimo|Presto|Prestiss\.|accel\.|rit\.|rall\.|riten\.|string\.|allarg\.|a tempo|Tempo I|rubato|meno mosso|più mosso|cres\.|<|decresc\.|dim\.|>|rfz|fp|pf|sub\.p|sub\.f)""",RegexOption.IGNORE_CASE) -private val musicForEmmentRegex = Regex("""^(ppp|pp|mp|mf|fff|ff|p|f|sfz|sf|fz|rfz|fp|pf)\b""",RegexOption.IGNORE_CASE) +private val musicForEmmentRegex = Regex( + """^(?:[mfp]+|sfz|sf|fz|rfz)$""", + RegexOption.IGNORE_CASE +) + + private val blankPrefixRegex =Regex("""z([0-9A-Z])[:]""") private val noteRegex = Regex("([drmfsltDRFSTw][ia]?|―)[0-9'¹²³⁴₁₂₃₄,]*") private val inclusionRegex = Regex("^I([0-9]):(.*)") @@ -628,7 +633,7 @@ fun LazyVerticalGridTUO( tuoList.drop(1).mapIndexedNotNull { globalIndex, oneTUO -> val rawMarker = oneTUO.pTemplate.markerToString() val hairPin = oneTUO.hasHairPin() - val markerList = sharedScreenModel.parseMarkers(rawMarker) + val markerList = sharedScreenModel.parseMarkers(rawMarker).toMutableList() /*val finalMarker = when { hairPin != null && markerText.isBlank() -> hairPin.toString() hairPin != null && markerText.isNotBlank() -> "${markerText.trim()}$hairPin" @@ -1271,7 +1276,7 @@ private fun TuoHeaderDecorations( markerFontFamily = ptSerifBoldItalic } - musicForEmmentRegex.matches(marker) -> { + musicForEmmentRegex.containsMatchIn(marker) -> { markerFontSize = MaterialTheme.typography.titleMedium.fontSize.value + 11 markerFontFamily = emmentaler } 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 ddc3a34..af6cd0a 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/viewmodel/SharedScreenModel.kt @@ -557,64 +557,8 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode return _tuoList.value.drop(1).size - 1 } - fun parseMarkers(rawText: String): MutableList { - val tokens = mutableListOf() - if (rawText.isBlank()) return tokens + fun parseMarkers(rawText: String): List = mg.dot.feufaro.solfa.parseMarkers(rawText) - var remaining = rawText.replace(",", " ") - .replace(Regex("""\b(DC)\b""", RegexOption.IGNORE_CASE), "D.C.") - .replace(Regex("""\b(DS)\b""", RegexOption.IGNORE_CASE), "D.S.") - .replace(Regex("""(D\.?C\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.C.Fin") - .replace(Regex("""(D\.?S\.?)\s*(Fin)""", RegexOption.IGNORE_CASE), "D.S.Fin") - .replace(Regex("""(D\.?C\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.C.alCoda") - .replace(Regex("""(D\.?S\.?)\s*(al\s*Coda)""", RegexOption.IGNORE_CASE), "D.S.alCoda") - - val allStaticMarkers = getAllMarker().sortedByDescending { it.length } - - val tempoRegex = Regex("""^(♩\s*=\s*\d+)""", RegexOption.IGNORE_CASE) - val modulaRegex = Regex("""^(D(?:ô|o)?\s*dia\s*(?:C|Db|D|Eb|E|F|Gb|G|Ab|A|Bb|B))""", RegexOption.IGNORE_CASE) - val measureRegex = Regex("""^\d+\s*/\s*\d+""") - // Lexer - while (remaining.isNotEmpty()) { - remaining = remaining.trimStart() - if (remaining.isEmpty()) break - - val tempoMatch = tempoRegex.find(remaining) - if (tempoMatch != null) { - tokens.add(tempoMatch.value) - remaining = remaining.substring(tempoMatch.value.length) - continue - } - - val modulaMatch = modulaRegex.find(remaining) - if (modulaMatch != null) { - tokens.add(modulaMatch.value) - remaining = remaining.substring(modulaMatch.value.length) - continue - } - - val measureMatch = measureRegex.find(remaining) - if (measureMatch != null) { - val cleanMeasure = measureMatch.value.replace(" ", "") - tokens.add(cleanMeasure) - remaining = remaining.substring(measureMatch.value.length) - continue - } - - val matchedMarker = allStaticMarkers.find { remaining.startsWith(it, ignoreCase = true) } - if (matchedMarker != null) { - tokens.add(matchedMarker) - remaining = remaining.substring(matchedMarker.length) - } else { - val unknownChunk = remaining.takeWhile { !it.isWhitespace() } - tokens.add(unknownChunk) - remaining = remaining.substring(unknownChunk.length) - } - } - -// println("Sortie: ${tokens.joinToString(" | ")}") - return tokens - } fun updateAndFinalizeMidiData(rawList: List) { // val timestamps = _tuoTimestamps.value