musicXML solfa
This commit is contained in:
parent
a2e6070f95
commit
2657b64f35
67 changed files with 2547 additions and 3 deletions
|
|
@ -42,7 +42,9 @@ kotlin {
|
||||||
implementation(libs.koin.core) // Koin core for shared logic
|
implementation(libs.koin.core) // Koin core for shared logic
|
||||||
implementation(libs.koin.compose) // Koin for Compose Multiplatform UI
|
implementation(libs.koin.compose) // Koin for Compose Multiplatform UI
|
||||||
implementation(libs.koin.core.viewmodel) // Koin for KMP ViewModels
|
implementation(libs.koin.core.viewmodel) // Koin for KMP ViewModels
|
||||||
|
implementation(libs.core) // Dépendance sous-jacente pour XML
|
||||||
|
implementation(libs.serialization)
|
||||||
|
api(libs.kmp.observableviewmodel.core)
|
||||||
}
|
}
|
||||||
commonTest.dependencies {
|
commonTest.dependencies {
|
||||||
implementation(libs.kotlin.test)
|
implementation(libs.kotlin.test)
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,21 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||||
|
|
||||||
import feufaro.composeapp.generated.resources.Res
|
import feufaro.composeapp.generated.resources.Res
|
||||||
import feufaro.composeapp.generated.resources.compose_multiplatform
|
import feufaro.composeapp.generated.resources.compose_multiplatform
|
||||||
|
import org.koin.compose.koinInject
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun App() {
|
fun App() {
|
||||||
|
val fileRepository = koinInject<FileRepository>()
|
||||||
|
var fileContent by remember { mutableStateOf("Chargement asset ....") }
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
try {
|
||||||
|
fileContent = "xxx"+ fileRepository.readFileContent("assets://ews-1.txt")
|
||||||
|
}catch (e: Exception) {
|
||||||
|
fileContent = "Erreur : ${e.message}"
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
var showContent by remember { mutableStateOf(false) }
|
var showContent by remember { mutableStateOf(false) }
|
||||||
Column(
|
Column(
|
||||||
|
|
@ -29,6 +40,7 @@ fun App() {
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
|
Text(text = fileContent)
|
||||||
Button(onClick = { showContent = !showContent }) {
|
Button(onClick = { showContent = !showContent }) {
|
||||||
Text("Click me!")
|
Text("Click me!")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package mg.dot.feufaro
|
||||||
|
|
||||||
|
object CommonTools {
|
||||||
|
//companion object {
|
||||||
|
fun tonalityToNumber(tonality: String) : Int {
|
||||||
|
val result: Map<String, Int> = mapOf(
|
||||||
|
"C" to 1,
|
||||||
|
"C#" to 2, "Db" to 2,
|
||||||
|
"D" to 3,
|
||||||
|
"D#" to 4, "Eb" to 4,
|
||||||
|
"E" to 5,
|
||||||
|
"F" to 6,
|
||||||
|
"F#" to 7, "Gb" to 7,
|
||||||
|
"G" to 8,
|
||||||
|
"G#" to 9, "Ab" to 9,
|
||||||
|
"A" to 10,
|
||||||
|
"A#" to 11, "Bb" to 11,
|
||||||
|
"B" to 12
|
||||||
|
)
|
||||||
|
if (result.containsKey(tonality)) {
|
||||||
|
return result[tonality]!!
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
fun numberToTonality(number: Int): String {
|
||||||
|
val result: Array<String> = arrayOf("", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
|
||||||
|
return if (number in 1..12) {
|
||||||
|
result[number]
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
package mg.dot.feufaro
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import com.rickclephas.kmp.observableviewmodel.ViewModel
|
||||||
|
import com.rickclephas.kmp.observableviewmodel.MutableStateFlow
|
||||||
|
import mg.dot.feufaro.solfa.TimeUnitObject
|
||||||
|
|
||||||
|
open class SharedViewModel(): ViewModel() {
|
||||||
|
private val _data = MutableStateFlow<String>(viewModelScope, "Next ...")
|
||||||
|
val data: StateFlow<String> = _data.asStateFlow()
|
||||||
|
private val _measure = MutableStateFlow<String>(viewModelScope, "")
|
||||||
|
val measure: StateFlow<String> = _measure.asStateFlow()
|
||||||
|
private val _songTitle = MutableStateFlow<String>(viewModelScope, "")
|
||||||
|
val songTitle: StateFlow<String> = _songTitle.asStateFlow()
|
||||||
|
private val _stanza = MutableStateFlow<Int>(viewModelScope, 0)
|
||||||
|
val stanza: StateFlow<Int> = _stanza.asStateFlow()
|
||||||
|
private val _songKey = MutableStateFlow<String>(viewModelScope, "")
|
||||||
|
val songKey: StateFlow<String> = _songKey.asStateFlow()
|
||||||
|
private val _songAuthor = MutableStateFlow<String>(viewModelScope,"")
|
||||||
|
val songAuthor: StateFlow<String> = _songAuthor.asStateFlow()
|
||||||
|
private val _songComposer = MutableStateFlow<String>(viewModelScope,"")
|
||||||
|
val songComposer: StateFlow<String> = _songComposer.asStateFlow()
|
||||||
|
private val _songRhythm = MutableStateFlow<String>(viewModelScope,"")
|
||||||
|
val songRhythm: StateFlow<String> = _songRhythm.asStateFlow()
|
||||||
|
private val _nbStanzas = MutableStateFlow<Int>(viewModelScope,0)
|
||||||
|
val nbStanzas: StateFlow<Int> = _nbStanzas.asStateFlow()
|
||||||
|
private var _tuoList = MutableStateFlow<List<TimeUnitObject>>(viewModelScope, emptyList())
|
||||||
|
val tuoList: StateFlow<List<TimeUnitObject>> = _tuoList.asStateFlow()
|
||||||
|
private var _playlist = MutableStateFlow<List<String>>(viewModelScope, emptyList())
|
||||||
|
val playlist: StateFlow<List<String>> = _playlist.asStateFlow()
|
||||||
|
private val tempTimeUnitObjectList = mutableListOf<TimeUnitObject>()
|
||||||
|
var _hasMarker = MutableStateFlow<Boolean>(viewModelScope, false)
|
||||||
|
val hasMarker: StateFlow<Boolean> = _hasMarker.asStateFlow()
|
||||||
|
|
||||||
|
fun appendData(otherData: String) {
|
||||||
|
_data.value += otherData
|
||||||
|
}
|
||||||
|
fun reset() {
|
||||||
|
tempTimeUnitObjectList.clear()
|
||||||
|
}
|
||||||
|
fun lastTUO(): TimeUnitObject? {
|
||||||
|
return tempTimeUnitObjectList.lastOrNull()
|
||||||
|
}
|
||||||
|
fun addTUO(newTUO: TimeUnitObject) {
|
||||||
|
tempTimeUnitObjectList.add(newTUO)
|
||||||
|
}
|
||||||
|
fun doneTUOList() {
|
||||||
|
_tuoList.value = tempTimeUnitObjectList.toList()
|
||||||
|
}
|
||||||
|
fun setMeasure(theMeasure: String) {
|
||||||
|
_measure.value = theMeasure
|
||||||
|
}
|
||||||
|
fun setSongTitle(theTitle: String) {
|
||||||
|
_songTitle.value = theTitle
|
||||||
|
}
|
||||||
|
fun setStanza(theStanza: String) {
|
||||||
|
try {
|
||||||
|
_stanza.value = theStanza.toInt()
|
||||||
|
} catch(e: NumberFormatException) {
|
||||||
|
_stanza.value = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun setSongKey(theSongKey: String) {
|
||||||
|
_songKey.value = theSongKey
|
||||||
|
}
|
||||||
|
fun setSongAuthor(theSongAuthor: String) {
|
||||||
|
_songAuthor.value = theSongAuthor
|
||||||
|
}
|
||||||
|
fun setSongComposer(theSongComposer: String) {
|
||||||
|
_songComposer.value = theSongComposer
|
||||||
|
}
|
||||||
|
fun setSongRhythm(theSongRhythm: String) {
|
||||||
|
_songRhythm.value = theSongRhythm
|
||||||
|
}
|
||||||
|
fun setNbStanzas(nbStanzas: Int) {
|
||||||
|
_nbStanzas.value = nbStanzas
|
||||||
|
}
|
||||||
|
fun setPlaylist(thePlaylist: List<String>) {
|
||||||
|
_playlist.value = thePlaylist
|
||||||
|
}
|
||||||
|
fun setHasMarker(theHasMarker: Boolean) {
|
||||||
|
_hasMarker.value = theHasMarker
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
package mg.dot.feufaro.di
|
package mg.dot.feufaro.di
|
||||||
|
|
||||||
|
import mg.dot.feufaroo.musicXML.MusicXML
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
import org.koin.core.module.dsl.singleOf
|
||||||
|
|
||||||
val commonModule = module {
|
val commonModule = module {
|
||||||
// Déclarez FileRepository comme un singleton.
|
// Déclarez FileRepository comme un singleton.
|
||||||
// L'implémentation concrète (actual) sera résolue par Koin en fonction de la plateforme.
|
// L'implémentation concrète (actual) sera résolue par Koin en fonction de la plateforme.
|
||||||
// Pour Android, Koin injectera le Context que vous avez fourni via androidContext().
|
// Pour Android, Koin injectera le Context que vous avez fourni via androidContext().
|
||||||
|
singleOf(::MusicXML)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("attributes ", "", "")
|
||||||
|
data class MXAttributes (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("staff-details", "", "")
|
||||||
|
var staffDetails: MXStaffDetails? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("clef", "", "")
|
||||||
|
var clef: MXClef? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("time", "", "")
|
||||||
|
var time: MXTime? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("divisions", "", "")
|
||||||
|
var divisions: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("key", "", "")
|
||||||
|
var key: MXKey? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("backup ", "", "")
|
||||||
|
data class MXBackup (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("duration", "", "")
|
||||||
|
var duration: Int? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlValue
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("beam ", "", "")
|
||||||
|
data class MXBeam (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("number", "", "")
|
||||||
|
var number: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlValue
|
||||||
|
@XmlSerialName("content", "", "")
|
||||||
|
var content: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("clef ", "", "")
|
||||||
|
data class MXClef (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("line", "", "")
|
||||||
|
var line: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("sign", "", "")
|
||||||
|
var sign: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlValue
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("creator", "", "")
|
||||||
|
data class MXCreator(
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("type", "", "")
|
||||||
|
var type: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlValue
|
||||||
|
@XmlSerialName("content", "", "")
|
||||||
|
var content: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("credit ", "", "")
|
||||||
|
data class MXCredit (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("credit-type", "", "")
|
||||||
|
var creditType: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("page", "", "")
|
||||||
|
var page: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("credit-words", "", "")
|
||||||
|
var creditWords: MXCreditWords? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlValue
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("credit-words ", "", "")
|
||||||
|
data class MXCreditWords (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("font-weight", "", "")
|
||||||
|
var fontWeight: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("font-size", "", "")
|
||||||
|
var fontSize: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("font-family", "", "")
|
||||||
|
var fontFamily: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("default-y", "", "")
|
||||||
|
var defaultY: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("default-x", "", "")
|
||||||
|
var defaultX: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlValue
|
||||||
|
@XmlSerialName("content", "", "")
|
||||||
|
var content: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("defaults ", "", "")
|
||||||
|
data class MXDefaults (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("scaling", "", "")
|
||||||
|
var scaling: MXScaling? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("page-layout", "", "")
|
||||||
|
var pageLayout: MXPageLayout? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("lyric-font", "", "")
|
||||||
|
var lyricFont: MXLyricFont? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("direction ", "", "")
|
||||||
|
data class MXDirection (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("sound", "", "")
|
||||||
|
var sound: MXSound? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("placement", "", "")
|
||||||
|
var placement: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("direction-type", "", "")
|
||||||
|
var directionType: MXDirectionType? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("direction-type ", "", "")
|
||||||
|
data class MXDirectionType (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("words", "", "")
|
||||||
|
var words: String? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("encoding ", "", "")
|
||||||
|
data class MXEncoding (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("software", "", "")
|
||||||
|
var software: List<String> = listOf(),
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("encoding-date", "", "")
|
||||||
|
var encodingDate: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("supports", "", "")
|
||||||
|
var supports: List<MXSupports> = listOf()
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("forward ", "", "")
|
||||||
|
data class MXForward (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("duration", "", "")
|
||||||
|
var duration: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("voice", "", "")
|
||||||
|
var voice: Int? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("harmony ", "", "")
|
||||||
|
data class MXHarmony (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("kind", "", "")
|
||||||
|
var kind: MXKind? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("relative-x", "", "")
|
||||||
|
var relativeX: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("root", "", "")
|
||||||
|
var root: MXHarmonyRoot? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("font-size", "", "")
|
||||||
|
var fontSize: Double? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("default-y", "", "")
|
||||||
|
var defaultY: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("placement", "", "")
|
||||||
|
var placement: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("harmonyRoot ", "", "")
|
||||||
|
data class MXHarmonyRoot (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("root-step", "", "")
|
||||||
|
var rootStep: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("identification ", "", "")
|
||||||
|
data class MXIdentification (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("creator", "", "")
|
||||||
|
var creator: List<MXCreator> = listOf(),
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("miscellaneous", "", "")
|
||||||
|
var miscellaneous: MXMiscellaneous? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("source", "", "")
|
||||||
|
var source: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("encoding", "", "")
|
||||||
|
var encoding: MXEncoding? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("key ", "", "")
|
||||||
|
data class MXKey (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("fifths", "", "")
|
||||||
|
var fifths: Int? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlValue
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("kind ", "", "")
|
||||||
|
data class MXKind (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("text", "", "")
|
||||||
|
var text: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlValue
|
||||||
|
@XmlSerialName("content", "", "")
|
||||||
|
var content: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("lyric ", "", "")
|
||||||
|
data class MXLyric (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("number", "", "")
|
||||||
|
var number: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("syllabic", "", "")
|
||||||
|
var syllabic: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("default-y", "", "")
|
||||||
|
var defaultY: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("placement", "", "")
|
||||||
|
var placement: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("text", "", "")
|
||||||
|
var text: String? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("lyric-font ", "", "")
|
||||||
|
data class MXLyricFont (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("font-size", "", "")
|
||||||
|
var fontSize: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("font-family", "", "")
|
||||||
|
var fontFamily: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("measure ", "", "")
|
||||||
|
data class MXMeasure (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("number", "", "")
|
||||||
|
var number: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("note", "", "")
|
||||||
|
var note: List<MXNote> = listOf(),
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("print", "", "")
|
||||||
|
var print: MXPrint? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("width", "", "")
|
||||||
|
var width: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("attributes", "", "")
|
||||||
|
var attributes: MXAttributes? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("direction", "", "")
|
||||||
|
var direction: MXDirection? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("backup", "", "")
|
||||||
|
var backup: MXBackup? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("harmony", "", "")
|
||||||
|
var harmony: MXHarmony? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("forward", "", "")
|
||||||
|
var forward: MXForward? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("midi-instrument ", "", "")
|
||||||
|
data class MXMidiInstrument (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("volume", "", "")
|
||||||
|
var volume: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("midi-channel", "", "")
|
||||||
|
var midiChannel: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("id", "", "")
|
||||||
|
var id: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("midi-program", "", "")
|
||||||
|
var midiProgram: Int? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("miscellaneous ", "", "")
|
||||||
|
data class MXMiscellaneous (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("miscellaneous-field", "", "")
|
||||||
|
var miscellaneousField: List<MXMiscellaneousField> = listOf()
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlValue
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("miscellaneous-field ", "", "")
|
||||||
|
data class MXMiscellaneousField (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("name", "", "")
|
||||||
|
var name: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlValue
|
||||||
|
@XmlSerialName("content", "", "")
|
||||||
|
var content: String? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("notations ", "", "")
|
||||||
|
data class MXNotations (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("slur", "", "")
|
||||||
|
var slur: MXSlur? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("note ", "", "")
|
||||||
|
data class MXNote (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("duration", "", "")
|
||||||
|
var duration: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("voice", "", "")
|
||||||
|
var voice: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("lyric", "", "")
|
||||||
|
var lyric: List<MXLyric> = listOf(),
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("default-x", "", "")
|
||||||
|
var defaultX: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("pitch", "", "")
|
||||||
|
var pitch: MXPitch? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("type", "", "")
|
||||||
|
var type: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("stem", "", "")
|
||||||
|
var stem: MXStem? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("chord", "", "")
|
||||||
|
var chord: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("dot", "", "")
|
||||||
|
var dot: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("beam", "", "")
|
||||||
|
var beam: MXBeam? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("notations", "", "")
|
||||||
|
var notations: MXNotations? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("accidental", "", "")
|
||||||
|
var accidental: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("page-layout ", "", "")
|
||||||
|
data class MXPageLayout (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("page-margins", "", "")
|
||||||
|
var pageMargins: MXPageMargins? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("page-height", "", "")
|
||||||
|
var pageHeight: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("page-width", "", "")
|
||||||
|
var pageWidth: Int? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("page-margins ", "", "")
|
||||||
|
data class MXPageMargins (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("right-margin", "", "")
|
||||||
|
var rightMargin: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("left-margin", "", "")
|
||||||
|
var leftMargin: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("bottom-margin", "", "")
|
||||||
|
var bottomMargin: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("type", "", "")
|
||||||
|
var type: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("top-margin", "", "")
|
||||||
|
var topMargin: Int? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("part ", "", "")
|
||||||
|
data class MXPart (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("measure", "", "")
|
||||||
|
var measure: List<MXMeasure> = listOf(),
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("id", "", "")
|
||||||
|
var id: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("part-list ", "", "")
|
||||||
|
data class MXPartList (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("score-part", "", "")
|
||||||
|
var scorePart: List<MXScorePart> = listOf()
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("pitch ", "", "")
|
||||||
|
data class MXPitch (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("octave", "", "")
|
||||||
|
var octave: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("step", "", "")
|
||||||
|
var step: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("alter", "", "")
|
||||||
|
var alter: Int? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("print ", "", "")
|
||||||
|
data class MXPrint (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("system-layout", "", "")
|
||||||
|
var systemLayout: MXSystemLayout? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("measure-numbering", "", "")
|
||||||
|
var measureNumbering: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("staff-layout", "", "")
|
||||||
|
var staffLayout: MXStaffLayout? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("new-system", "", "")
|
||||||
|
var newSystem: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("root ", "", "")
|
||||||
|
data class MXRoot (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("score-partwise", "", "")
|
||||||
|
var scorePartwise: MXScorePartwise? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("scaling ", "", "")
|
||||||
|
data class MXScaling (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("millimeters", "", "")
|
||||||
|
var millimeters: Double? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("tenths", "", "")
|
||||||
|
var tenths: Int? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("score-instrument", "", "")
|
||||||
|
data class MXScoreInstrument (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("instrument-name", "", "")
|
||||||
|
var instrumentName: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("id", "", "")
|
||||||
|
var id: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("score-part ", "", "")
|
||||||
|
data class MXScorePart (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("midi-instrument", "", "")
|
||||||
|
var midiInstrument: MXMidiInstrument? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("score-instrument", "", "")
|
||||||
|
var scoreInstrument: MXScoreInstrument? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("id", "", "")
|
||||||
|
var id: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("part-name", "", "")
|
||||||
|
var partName: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("part-abbreviation", "", "")
|
||||||
|
var partAbbreviation: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("score-partwise", "", "")
|
||||||
|
data class MXScorePartwise (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("identification", "", "")
|
||||||
|
var identification: MXIdentification? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("defaults", "", "")
|
||||||
|
var defaults: MXDefaults? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("work", "", "")
|
||||||
|
var work: MXWork? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("part", "", "")
|
||||||
|
var part: List<MXPart> = listOf(),
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("credit", "", "")
|
||||||
|
var credit: List<MXCredit> = listOf(),
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("version", "", "")
|
||||||
|
var version: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("part-list", "", "")
|
||||||
|
var partList: MXPartList? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("slur ", "", "")
|
||||||
|
data class MXSlur (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("number", "", "")
|
||||||
|
var number: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("bezier-y", "", "")
|
||||||
|
var bezierY: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("bezier-x", "", "")
|
||||||
|
var bezierX: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("default-y", "", "")
|
||||||
|
var defaultY: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("default-x", "", "")
|
||||||
|
var defaultX: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("placement", "", "")
|
||||||
|
var placement: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("type", "", "")
|
||||||
|
var type: String? = null
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("sound ", "", "")
|
||||||
|
data class MXSound (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("tempo", "", "")
|
||||||
|
var tempo: Int? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("staff-details ", "", "")
|
||||||
|
data class MXStaffDetails (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("print-object", "", "")
|
||||||
|
var printObject: String? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("staff-layout ", "", "")
|
||||||
|
data class MXStaffLayout (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("number", "", "")
|
||||||
|
var number: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("staff-distance", "", "")
|
||||||
|
var staffDistance: Int? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlValue
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("stem ", "", "")
|
||||||
|
data class MXStem (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("default-y", "", "")
|
||||||
|
var defaultY: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlValue
|
||||||
|
@XmlSerialName("content", "", "")
|
||||||
|
var content: String? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("supports ", "", "")
|
||||||
|
data class MXSupports (
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("attribute", "", "")
|
||||||
|
var attribute: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("type", "", "")
|
||||||
|
var type: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("value", "", "")
|
||||||
|
var value: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("element", "", "")
|
||||||
|
var element: String? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("system-layout ", "", "")
|
||||||
|
data class MXSystemLayout (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("system-margins", "", "")
|
||||||
|
var systemMargins: MXSystemMargins? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("top-system-distance", "", "")
|
||||||
|
var topSystemDistance: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("system-distance", "", "")
|
||||||
|
var systemDistance: Int? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("system-margins ", "", "")
|
||||||
|
data class MXSystemMargins (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("right-margin", "", "")
|
||||||
|
var rightMargin: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("left-margin", "", "")
|
||||||
|
var leftMargin: Int? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("time ", "", "")
|
||||||
|
data class MXTime (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("beats", "", "")
|
||||||
|
var beats: Int? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("beat-type", "", "")
|
||||||
|
var beatType: Int? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlElement
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XmlSerialName
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@XmlSerialName("work ", "", "")
|
||||||
|
data class MXWork (
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("work-title", "", "")
|
||||||
|
var workTitle: String? = null,
|
||||||
|
@Serializable
|
||||||
|
@XmlElement
|
||||||
|
@XmlSerialName("work-number", "", "")
|
||||||
|
var workNumber: String? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package mg.dot.feufaroo.musicXML
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
|
import mg.dot.feufaro.FileRepository
|
||||||
|
import nl.adaptivity.xmlutil.serialization.XML
|
||||||
|
import org.koin.compose.koinInject
|
||||||
|
|
||||||
|
class MusicXML(private val fileRepository: FileRepository) {
|
||||||
|
val module = SerializersModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
val xml = XML (module) {
|
||||||
|
defaultPolicy {
|
||||||
|
ignoreUnknownChildren()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var xmlString = ""
|
||||||
|
suspend fun load() {
|
||||||
|
|
||||||
|
xmlString = fileRepository.readFileContent("assets://13.xml")
|
||||||
|
try {
|
||||||
|
val xmlObject = xml.decodeFromString<MXScorePartwise>(xmlString)
|
||||||
|
var keyFifths = 0
|
||||||
|
var timeIndex = 0
|
||||||
|
xmlObject.part.forEach { partNode ->
|
||||||
|
partNode.measure.forEach { measureNode ->
|
||||||
|
if (measureNode.backup?.duration != null) {
|
||||||
|
val backupDuration = measureNode.backup!!.duration!!
|
||||||
|
timeIndex -= backupDuration
|
||||||
|
}
|
||||||
|
if (measureNode.attributes?.key?.fifths != null) {
|
||||||
|
keyFifths = measureNode.attributes!!.key!!.fifths!!
|
||||||
|
}
|
||||||
|
val keySign = measureNode.attributes?.clef?.sign
|
||||||
|
val nominator = measureNode.attributes?.time?.beats
|
||||||
|
val denominator = measureNode.attributes?.time?.beatType
|
||||||
|
measureNode.note.forEach { noteNode ->
|
||||||
|
val voiceNumber = noteNode.voice
|
||||||
|
val duration = noteNode.duration
|
||||||
|
val chord = noteNode.chord
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e: Exception) {
|
||||||
|
println("${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val parseScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
parseScope.launch {
|
||||||
|
load()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
enum class LineType {
|
||||||
|
ROUND,
|
||||||
|
SQUARE
|
||||||
|
}
|
||||||
|
class UnderlineSpec (
|
||||||
|
var x: Int,
|
||||||
|
val lineType: LineType
|
||||||
|
) {
|
||||||
|
var y: Int = -1
|
||||||
|
var okX = false
|
||||||
|
}
|
||||||
|
class AnnotatedTUO (originalString: String, val voiceNumber: Int){
|
||||||
|
var finalText: String = ""
|
||||||
|
companion object {
|
||||||
|
var inRoundParen = mutableListOf<Boolean>()
|
||||||
|
var inSquareParen = mutableListOf<Boolean>()
|
||||||
|
}
|
||||||
|
var underlineSpec = mutableListOf<UnderlineSpec>()
|
||||||
|
private fun toggleInRoundParen(voiceNumber: Int, value: Boolean, x: Int) {
|
||||||
|
if (value && (
|
||||||
|
(inRoundParen.size > voiceNumber && inRoundParen[voiceNumber])
|
||||||
|
|| (inSquareParen.size > voiceNumber && inSquareParen[voiceNumber]) )) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!value && (inRoundParen.size <= voiceNumber || !inRoundParen[voiceNumber])) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
while (inRoundParen.size <= voiceNumber) {
|
||||||
|
inRoundParen.add(false)
|
||||||
|
}
|
||||||
|
inRoundParen[voiceNumber] = value
|
||||||
|
if (value) {
|
||||||
|
underlineSpec.add(UnderlineSpec(x, LineType.ROUND))
|
||||||
|
} else {
|
||||||
|
underlineSpec.lastOrNull()?.y = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun toggleInSquareParen(voiceNumber: Int, value: Boolean, x: Int) {
|
||||||
|
if (value && (
|
||||||
|
(inRoundParen.size > voiceNumber && inRoundParen[voiceNumber])
|
||||||
|
|| (inSquareParen.size > voiceNumber && inSquareParen[voiceNumber]) )) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!value && (inSquareParen.size <= voiceNumber || !inSquareParen[voiceNumber])) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
while (inSquareParen.size <= voiceNumber) {
|
||||||
|
inSquareParen.add(false)
|
||||||
|
}
|
||||||
|
inSquareParen[voiceNumber] = value
|
||||||
|
if (value) {
|
||||||
|
underlineSpec.add(UnderlineSpec(x, LineType.SQUARE))
|
||||||
|
} else {
|
||||||
|
underlineSpec.lastOrNull()?.y = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init {
|
||||||
|
var x = -1
|
||||||
|
if (voiceNumber > 0) {
|
||||||
|
if (inRoundParen.size > voiceNumber && inRoundParen[voiceNumber]) {
|
||||||
|
underlineSpec.add(UnderlineSpec(0, LineType.ROUND))
|
||||||
|
}
|
||||||
|
if (inSquareParen.size > voiceNumber && inSquareParen[voiceNumber]) {
|
||||||
|
underlineSpec.add(UnderlineSpec(0, LineType.SQUARE))
|
||||||
|
}
|
||||||
|
originalString.toCharArray().forEach {
|
||||||
|
when (it) {
|
||||||
|
'(' -> toggleInRoundParen(voiceNumber, true, x)
|
||||||
|
'[' -> toggleInSquareParen(voiceNumber, true, x)
|
||||||
|
')' -> toggleInRoundParen(voiceNumber, false, x)
|
||||||
|
']' -> toggleInSquareParen(voiceNumber, false, x)
|
||||||
|
else -> {
|
||||||
|
x++
|
||||||
|
finalText += it
|
||||||
|
if (it in listOf('d', 'r', 'm', 'f', 's', 'l', 't')) {
|
||||||
|
val lastUnderLineSpec = underlineSpec.lastOrNull()
|
||||||
|
if (lastUnderLineSpec?.okX == false) {
|
||||||
|
lastUnderLineSpec.okX = true
|
||||||
|
lastUnderLineSpec.x = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
class Lyrics (val text: String, val stanza: Int, val refrain: Boolean = false, val stanzaDSIn: Int = -1){
|
||||||
|
val stanzaDS = if (stanzaDSIn == -1) { stanza } else { stanzaDSIn }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
class Markers {
|
||||||
|
var marker: String = ""
|
||||||
|
fun unMark(mark: Char) {
|
||||||
|
marker.replace(mark.toString(), "", ignoreCase = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
class Note (val note: String, private var separator: String= "") {
|
||||||
|
|
||||||
|
private var duration: Int = 0
|
||||||
|
var marker: MutableList<String> = mutableListOf()
|
||||||
|
var markerBefore: String = ""
|
||||||
|
fun addMarker(newMarker: String) {
|
||||||
|
if (marker.contains(newMarker)) return
|
||||||
|
marker.add(newMarker)
|
||||||
|
}
|
||||||
|
fun setMarkers(newMarkers: MutableList<String>) {
|
||||||
|
marker = newMarkers
|
||||||
|
}
|
||||||
|
fun markBefore(newMarker: String) {
|
||||||
|
markerBefore += newMarker
|
||||||
|
}
|
||||||
|
/*fun unMark(badMarker: String) {
|
||||||
|
if (marker.contains(badMarker)) {
|
||||||
|
marker.remove(badMarker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun setSeparator(newSeparator : String) {
|
||||||
|
separator = newSeparator
|
||||||
|
}*/
|
||||||
|
private fun String.asMarker(): String {
|
||||||
|
if (this == "(") return ""
|
||||||
|
if (this == ",") return " ,"
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
override fun toString(): String {
|
||||||
|
var result = ""
|
||||||
|
if (marker.contains("(")) {
|
||||||
|
result += "("
|
||||||
|
}
|
||||||
|
result += markerBefore.toCharArray().joinToString("") { it.toString().asMarker() } + note + marker.joinToString(""){ it.asMarker() }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
class PNotes (var note: String, private var markers: MutableList<String> = mutableListOf("")) {
|
||||||
|
fun addMarker(newMarker: String) {
|
||||||
|
markers.add(newMarker)
|
||||||
|
}
|
||||||
|
fun hasMarker(marker: String): Boolean {
|
||||||
|
return markers.contains(marker)
|
||||||
|
}
|
||||||
|
fun toNote(): Note {
|
||||||
|
val newNote = Note(note)
|
||||||
|
newNote.setMarkers(markers)
|
||||||
|
return newNote
|
||||||
|
}
|
||||||
|
// appendNote is used in POneVoiceNote.appendLastNote()
|
||||||
|
fun appendNote(noteSuffix: Char) {
|
||||||
|
note += noteSuffix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
class POneStanzaLyrics {
|
||||||
|
private val lyrics: MutableList<String> = mutableListOf("")
|
||||||
|
fun setLyrics(stanzaNumber: Int, lyrics: String) {
|
||||||
|
while (this.lyrics.size <= stanzaNumber) {
|
||||||
|
this.lyrics.add("")
|
||||||
|
}
|
||||||
|
this.lyrics[stanzaNumber] = lyrics
|
||||||
|
}
|
||||||
|
override fun toString(): String {
|
||||||
|
return lyrics[1]?: ""
|
||||||
|
}
|
||||||
|
fun appendDSLyrics(stanzaNumber: Int, lyrics: String) {
|
||||||
|
if (this.lyrics.size > stanzaNumber) {
|
||||||
|
this.lyrics[stanzaNumber] += "\n$lyrics"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun toList(stanzaNumber: Int): List<String> {
|
||||||
|
return if (this.lyrics.size > stanzaNumber) {
|
||||||
|
this.lyrics[stanzaNumber].split("\n")
|
||||||
|
} else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
class POneVoiceNote {
|
||||||
|
val oneVoiceNote: MutableList<PNotes> = mutableListOf()
|
||||||
|
companion object {
|
||||||
|
var nextMarkers: MutableList<String> = mutableListOf()
|
||||||
|
}
|
||||||
|
private var nextNote: String = ""
|
||||||
|
fun addNextMarker(marker: Char) {
|
||||||
|
nextMarkers.add(marker.toString())
|
||||||
|
}
|
||||||
|
fun appendLastNote(note: Char) {
|
||||||
|
oneVoiceNote.lastOrNull()?.appendNote(note)
|
||||||
|
}
|
||||||
|
fun appendLastMarker(marker: Char) {
|
||||||
|
oneVoiceNote.lastOrNull()?.addMarker(marker.toString())
|
||||||
|
}
|
||||||
|
fun addItem(noteString: String = "") {
|
||||||
|
val newNote: PNotes = PNotes(noteString, nextMarkers)
|
||||||
|
oneVoiceNote.add(newNote)
|
||||||
|
nextNote = ""
|
||||||
|
nextMarkers = mutableListOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
class PTemplate (val template: String, val separatorAfter: String, private val markers: MutableList<String> = mutableListOf("")){
|
||||||
|
fun addMarker(newMarker: String) {
|
||||||
|
markers.add(newMarker)
|
||||||
|
}
|
||||||
|
fun hasMarker(marker: String): Boolean {
|
||||||
|
return markers.contains(marker)
|
||||||
|
}
|
||||||
|
fun markerToString(): String {
|
||||||
|
if (markers.size == 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return markers.joinToString(separator = "") {
|
||||||
|
it.replace(Regex("^\\$\\{([a-z]:)?(.*)\\}"), "$2")
|
||||||
|
.replace("\$Q", "\uD834\uDD10")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
class ParseULine (var line: String, var measure: Int) {
|
||||||
|
private var cursorX = 0
|
||||||
|
var parsedString = ""
|
||||||
|
private var afterDollar = false
|
||||||
|
private var inComment = false
|
||||||
|
private var charX = ' '
|
||||||
|
companion object {
|
||||||
|
val mapXLate = mutableMapOf<Char, List<String>>(
|
||||||
|
'1' to listOf("," , "." , "," , ":"),
|
||||||
|
'2' to listOf("." , ".-," , ":" , ":-,"),
|
||||||
|
'3' to listOf(".-," , ".-:" , ":-," , ":-."),
|
||||||
|
'4' to listOf(":" , ".-;-," , ":-." , ":-.-,"),
|
||||||
|
'y' to listOf(".,D:", ".-,:D,", ":,D.", ":-,.D,")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parsed() : String {
|
||||||
|
val matchResult = Regex("^z([048C]):(.+)$").find(line)
|
||||||
|
if (matchResult != null) {
|
||||||
|
val capturedChar = matchResult.groups[1]!!.value
|
||||||
|
cursorX = when (capturedChar) {
|
||||||
|
"C" -> 12
|
||||||
|
else -> capturedChar.toInt()
|
||||||
|
}
|
||||||
|
line = matchResult.groups[2]!!.value
|
||||||
|
parsedString = ":"
|
||||||
|
}
|
||||||
|
parseUStage1()
|
||||||
|
parseUStage3()
|
||||||
|
return parsedString
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseUStage1() {
|
||||||
|
val parsedChars = line
|
||||||
|
// Beware, Android Regex requires "}" to be escaped!!!
|
||||||
|
.replace(Regex("\\$\\{x([1-9]\\d*)\\}(.*?)\\$\\{x0\\}")) { matchResult ->
|
||||||
|
val (nbIteration, iterable) = matchResult.destructured
|
||||||
|
val nTimes = nbIteration.toInt()
|
||||||
|
iterable.repeat(nTimes)
|
||||||
|
}.toCharArray()
|
||||||
|
var actualComment = ""
|
||||||
|
parsedChars.forEach {
|
||||||
|
if (inComment) {
|
||||||
|
parsedString += it
|
||||||
|
if (it == '}') {
|
||||||
|
inComment = false
|
||||||
|
val matchMeasure = Regex("^m:(\\d+)(/.*)?").find(actualComment)
|
||||||
|
if (matchMeasure != null) {
|
||||||
|
measure = matchMeasure.groups[1]!!.value.toInt()
|
||||||
|
}
|
||||||
|
actualComment = ""
|
||||||
|
} else {
|
||||||
|
actualComment += it
|
||||||
|
}
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
if (afterDollar) {
|
||||||
|
if (it == '{') {
|
||||||
|
inComment = true
|
||||||
|
}
|
||||||
|
afterDollar = false
|
||||||
|
parsedString += it
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
if (charX != 'z') {
|
||||||
|
charX = 'D'
|
||||||
|
}
|
||||||
|
when (it) {
|
||||||
|
'$' -> run {
|
||||||
|
afterDollar = !afterDollar
|
||||||
|
parsedString += "$"
|
||||||
|
}
|
||||||
|
'z', '-' -> run {
|
||||||
|
charX = it
|
||||||
|
}
|
||||||
|
'W' -> repeat(8) { parseUStage2('4')}
|
||||||
|
'G' -> repeat(4) { parseUStage2('4') }
|
||||||
|
'C' -> repeat(3) { parseUStage2('4') }
|
||||||
|
'8' -> repeat(2) { parseUStage2('4') }
|
||||||
|
'4', '3', '2', '1', 'y' -> parseUStage2(it)
|
||||||
|
'6' -> repeat(2) { parseUStage2('3')}
|
||||||
|
'9' -> repeat(3) { parseUStage2('3')}
|
||||||
|
'A' -> run {
|
||||||
|
parseUStage2('4')
|
||||||
|
parseUStage2('4')
|
||||||
|
parseUStage2('2')
|
||||||
|
}
|
||||||
|
'(', ')', '/' -> parsedString += it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun parseUStage2(codeChar : Char) {
|
||||||
|
val indexByFour = cursorX % 4
|
||||||
|
var sequence = mapXLate[codeChar]!![indexByFour]!!
|
||||||
|
if (charX == 'z') {
|
||||||
|
sequence = sequence.replace('-', charX)
|
||||||
|
}
|
||||||
|
cursorX += codeChar.toString().toInt()
|
||||||
|
if (sequence.contains(':')) {
|
||||||
|
val bar: Int = (cursorX / 4) % measure
|
||||||
|
var replacement = ':'
|
||||||
|
if ( (bar == 2 && measure == 4 )
|
||||||
|
|| (bar == 3 && measure in listOf(6, 9, 12))
|
||||||
|
|| (bar in listOf(6, 9) && measure in listOf(9, 12)))
|
||||||
|
{
|
||||||
|
replacement = '!'
|
||||||
|
}
|
||||||
|
if (bar == 0) {
|
||||||
|
replacement = '|'
|
||||||
|
}
|
||||||
|
if (replacement != ':') {
|
||||||
|
sequence = sequence.replace(':', replacement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var noteChar = 'D'
|
||||||
|
if (charX in listOf('z', '-')) {
|
||||||
|
noteChar = charX
|
||||||
|
}
|
||||||
|
parsedString += noteChar + sequence
|
||||||
|
charX = '-'
|
||||||
|
}
|
||||||
|
private fun parseUStage3() {
|
||||||
|
parsedString = parsedString.replace("-,-", "")
|
||||||
|
.replace(Regex("([:!|])-,-([:!|])"), "$1-$2")
|
||||||
|
.replace(Regex("([:!|])-\\.-([:!|])"), "$1-$2")
|
||||||
|
.replace(Regex("[\\.,]-([:!|])"), "$1")
|
||||||
|
.replace(Regex("[\\.,]([:!|])"), "$1")
|
||||||
|
.replace(Regex("([:!|])\\)/"), ")$1/")
|
||||||
|
.replace(Regex("[:!|]/"), "/")
|
||||||
|
.replace(Regex("/[:!|]"), "/")
|
||||||
|
.replace(Regex("[z0]([:!|])"), "$1")
|
||||||
|
}
|
||||||
|
}
|
||||||
520
composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt
Normal file
520
composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt
Normal file
|
|
@ -0,0 +1,520 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import mg.dot.feufaro.FileRepository
|
||||||
|
import mg.dot.feufaro.CommonTools
|
||||||
|
import mg.dot.feufaro.SharedViewModel
|
||||||
|
//import mg.dot.feufaro.getOpt
|
||||||
|
|
||||||
|
class Solfa(val sharedViewModel: SharedViewModel, private val fileRepository: FileRepository) {
|
||||||
|
private val T: MutableList<PTemplate> = mutableListOf()
|
||||||
|
private val N: MutableList<POneVoiceNote> = mutableListOf()
|
||||||
|
private val L: MutableList<POneStanzaLyrics> = mutableListOf()
|
||||||
|
private val unparsedNote = mutableListOf<String>()
|
||||||
|
companion object {
|
||||||
|
val REGEX_SHIFT_PAREN_UPWARD = Regex("([^a-yA-Y\\(\\[]+)([\\)\\]])")
|
||||||
|
val REGEX_SHIFT_PAREN_DOWNWARD = Regex("([\\(\\[])([^a-yA-y\\)\\]]+)")
|
||||||
|
val REGEX_COLLAPSE_PARENS_OPEN = Regex("([\\(\\[])[\\(\\[]+")
|
||||||
|
val REGEX_COLLAPSE_PARENS_CLOSE = Regex("([\\)\\]])[\\)\\]]+")
|
||||||
|
val REGEX_SINGLETON_PAREN = Regex("[\\(\\[](.[¹²³₁₂₃]?)?[\\)\\]]") // @todo
|
||||||
|
val REGEX_SHIFT_COMMA_UPWARD = Regex("([^a-zA-Z]+)([',])")
|
||||||
|
val REGEX_TEMPLATE_COMMENT = Regex("(\\$[A-Z]|\\$\\{[^\\}]*\\})")
|
||||||
|
val REGEX_REPETITION = Regex("([zdrmfslt-](?>[',]*))([1-9][0-9]*)")
|
||||||
|
val REGEX_PARSE_META = Regex("\\|(?=[a-z]:)")
|
||||||
|
val REGEX_LYRICS_COMMENT = Regex("\\$\\{([^\\}]*)\\}")
|
||||||
|
val REGEX_LYRICS_REPETITION = Regex("_(\\d)")
|
||||||
|
val REGEX_VOWELS_STAGE1 = Regex("[aeiouyàéỳ,;\\.\\-:!](?![ aeiouyàéỳ,;\\.\\-:!])", RegexOption.IGNORE_CASE)
|
||||||
|
val REGEX_VOWELS_STAGE2 = Regex("([aeiouyàéỳ,;\\.\\-:!])__", RegexOption.IGNORE_CASE)
|
||||||
|
val REGEX_MALAGASY_MN = Regex("([aeio])_([nm])([tdjkbp])")
|
||||||
|
val REGEX_MALAGASY_MN_DASHED = Regex("([aeio][nm])\\-([tdjkbp])")
|
||||||
|
}
|
||||||
|
var nextTIndex: Int = -1
|
||||||
|
var nextNIndex: Int = -1
|
||||||
|
var nextLIndex: Int = -1
|
||||||
|
var inGroup: Boolean = false
|
||||||
|
var templateString: String = ""
|
||||||
|
var nextPlayed = -1
|
||||||
|
|
||||||
|
private val meta: MutableMap<String, String> = mutableMapOf()
|
||||||
|
private val lyricsComment: MutableList<String> = mutableListOf()
|
||||||
|
suspend fun nextTimeUnitObject() {
|
||||||
|
val lastTUO = sharedViewModel.lastTUO()
|
||||||
|
nextTIndex++
|
||||||
|
if (nextTIndex == 5) {
|
||||||
|
sharedViewModel.doneTUOList()
|
||||||
|
}
|
||||||
|
if (T.getOrNull(nextTIndex) == null) {
|
||||||
|
sharedViewModel.doneTUOList()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val pTemplate = T[nextTIndex]
|
||||||
|
val unitObject = TimeUnitObject(pTemplate, lastTUO)
|
||||||
|
val templateCharArray = pTemplate.template.toCharArray()
|
||||||
|
var nextMarker: Char = '0'
|
||||||
|
templateCharArray.forEach { // D.-R
|
||||||
|
when (it) {
|
||||||
|
'D', 'R', 'M', 'F', 'S', 'L', 'T',
|
||||||
|
'd', 'r', 'm', 'f', 's', 'l', 't', 'w' -> {
|
||||||
|
nextNIndex++
|
||||||
|
if (!inGroup) {
|
||||||
|
nextLIndex ++
|
||||||
|
if (L.size > nextLIndex) {
|
||||||
|
unitObject.addLyrics(L[nextLIndex])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
N.forEachIndexed { voiceNumber, pOneVoiceNote -> run {
|
||||||
|
unitObject.addNote(voiceNumber, pOneVoiceNote.oneVoiceNote, nextNIndex, nextMarker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nextMarker = '0'
|
||||||
|
}
|
||||||
|
'.', ',', ->
|
||||||
|
unitObject.addMarker(it)
|
||||||
|
'-', 'Z' ->
|
||||||
|
N.indices.forEach { voiceNumber ->
|
||||||
|
unitObject.addBlank(voiceNumber, it)
|
||||||
|
}
|
||||||
|
'(', '[' -> {
|
||||||
|
nextLIndex++
|
||||||
|
if (L.size > nextLIndex) {
|
||||||
|
unitObject.addLyrics(L[nextLIndex])
|
||||||
|
}
|
||||||
|
inGroup = true
|
||||||
|
}
|
||||||
|
')', ']' -> {
|
||||||
|
inGroup = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
sharedViewModel.addTUO(unitObject)
|
||||||
|
}
|
||||||
|
nextTimeUnitObject()
|
||||||
|
}
|
||||||
|
fun loadSolfa(sourceFileName: String) {
|
||||||
|
sharedViewModel.reset()
|
||||||
|
parse(sourceFileName)
|
||||||
|
}
|
||||||
|
fun loadNextInPlaylist() {
|
||||||
|
val playlist = sharedViewModel?.playlist?.value ?: listOf()
|
||||||
|
if (playlist.isNotEmpty()) {
|
||||||
|
nextPlayed = (nextPlayed + 1) % playlist.size
|
||||||
|
loadSolfa(playlist[nextPlayed])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun parse(sourceFile: String) {
|
||||||
|
val parseScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
parseScope.launch {
|
||||||
|
val lines = try {
|
||||||
|
fileRepository.readFileLines(sourceFile)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("Opening $sourceFile raised exception {${e.message}")
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
T.clear()
|
||||||
|
N.clear()
|
||||||
|
L.clear()
|
||||||
|
unparsedNote.clear()
|
||||||
|
templateString = ""
|
||||||
|
nextTIndex = -1
|
||||||
|
nextNIndex = -1
|
||||||
|
nextLIndex = -1
|
||||||
|
lyricsComment.clear()
|
||||||
|
sharedViewModel.setStanza("1")
|
||||||
|
sharedViewModel.setNbStanzas(0)
|
||||||
|
TimeUnitObject.hasMarker(false)
|
||||||
|
lines.forEach { line ->
|
||||||
|
run {
|
||||||
|
parseOneLine(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unparsedNote.forEach {
|
||||||
|
preloadN(it)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
nextTimeUnitObject()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("Erreur parseScope Solfa:150 : ${e.message} iter: ${TimeUnitObject.nbBlock}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun rearrangeNote(noteString: String, infiniteIter: Int = 0): String {
|
||||||
|
var result: String = noteString
|
||||||
|
result = result .replace(REGEX_SHIFT_PAREN_UPWARD, "$2$1")
|
||||||
|
result = result .replace(REGEX_SHIFT_PAREN_DOWNWARD, "$2$1")
|
||||||
|
result = result .replace(REGEX_COLLAPSE_PARENS_OPEN, "$1")
|
||||||
|
result = result .replace(REGEX_COLLAPSE_PARENS_CLOSE, "$1")
|
||||||
|
result = result .replace(REGEX_SINGLETON_PAREN, "$1")
|
||||||
|
result = result .replace(REGEX_SHIFT_COMMA_UPWARD, "$2$1")
|
||||||
|
if (result != noteString && infiniteIter < 75) {
|
||||||
|
result = rearrangeNote(result, infiniteIter+1)
|
||||||
|
}
|
||||||
|
if (infiniteIter >= 70) {
|
||||||
|
println("$infiniteIter\n$noteString\n$result")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
private fun preloadN(noteLine: String) {
|
||||||
|
val voiceNumber = noteLine.substring(0, 1).toInt()
|
||||||
|
val templateStripped = REGEX_TEMPLATE_COMMENT.replace(templateString, "")
|
||||||
|
val templateCharArray = templateStripped.toCharArray()
|
||||||
|
val lineRepeated = REGEX_REPETITION.replace(noteLine.substring(1)) { matchResult ->
|
||||||
|
val (charIterated, iteration) = matchResult.destructured
|
||||||
|
val nTimes = iteration.toInt()
|
||||||
|
charIterated.repeat(nTimes)
|
||||||
|
}.replace(" ", "")
|
||||||
|
var noteChar2 = ""
|
||||||
|
var anchor = "d"
|
||||||
|
var inAnchor = false
|
||||||
|
var anchorNote = ' '
|
||||||
|
lineRepeated.toCharArray().forEach {
|
||||||
|
if (inAnchor) {
|
||||||
|
if (anchor.isEmpty()) {
|
||||||
|
anchorNote = it
|
||||||
|
}
|
||||||
|
if (anchor.isNotEmpty() && it !in listOf(',', '\'')) {
|
||||||
|
inAnchor = false
|
||||||
|
} else {
|
||||||
|
anchor += it
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (it == '#') {
|
||||||
|
inAnchor = true
|
||||||
|
anchor = ""
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
noteChar2 += it
|
||||||
|
val anchorSuffix = anchor.replace(Regex("^[drmfslt]"), "")
|
||||||
|
if (it in listOf('d', 'r', 'm', 'f', 's', 'l', 't', 'D', 'R', 'M', 'F', 'S', 'L', 'T')) {
|
||||||
|
noteChar2 += anchorSuffix
|
||||||
|
if (
|
||||||
|
(anchorNote == 'r' && it in listOf('d', 'D'))
|
||||||
|
|| (anchorNote == 'm' && it in listOf('d', 'D', 'r', 'R'))
|
||||||
|
|| (anchorNote == 'f' && it in listOf('d', 'D', 'r', 'R', 'm', 'M'))
|
||||||
|
|| (anchorNote == 's' && it !in listOf('s', 'S', 'l', 'L', 't', 'T'))
|
||||||
|
|| (anchorNote == 'l' && it !in listOf( 'l', 'L', 't', 'T'))
|
||||||
|
|| (anchorNote == 't' && it !in listOf( 't', 'T'))
|
||||||
|
) {
|
||||||
|
noteChar2 += '\''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
noteChar2 = noteChar2.replace(",'", "")
|
||||||
|
.replace("',", "")
|
||||||
|
.replace(",'", "")
|
||||||
|
.replace("',", "")
|
||||||
|
|
||||||
|
val noteCharIterator = noteChar2.toCharArray().iterator()
|
||||||
|
var result = ""
|
||||||
|
var firstInLoop = true
|
||||||
|
var replacement : Char = 'x'
|
||||||
|
templateCharArray.forEach {
|
||||||
|
when (it) {
|
||||||
|
'D', 'R', 'M', 'F', 'S', 'L', 'T',
|
||||||
|
'd', 'r', 'm', 'f', 's', 'l', 't', 'w' -> {
|
||||||
|
if (firstInLoop) {
|
||||||
|
replacement = if (noteCharIterator.hasNext()) noteCharIterator.next() else 'd'
|
||||||
|
firstInLoop = false
|
||||||
|
}
|
||||||
|
for (repetition in 0 until 6) {
|
||||||
|
result += replacement
|
||||||
|
replacement = if (noteCharIterator.hasNext()) noteCharIterator.next() else 'd'
|
||||||
|
if (replacement in setOf('d', 'r', 'm', 'f', 's', 'l', 't' , 'z',
|
||||||
|
'D', 'R', 'M', 'F', 'S', 'L', 'T', '-', 'Z')) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'/', '-', 'z', 'Z' -> {}
|
||||||
|
',' -> result += ";"
|
||||||
|
else -> result += it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = rearrangeNote(result)
|
||||||
|
loadN(voiceNumber, result)
|
||||||
|
}
|
||||||
|
private fun parseOneLine(line: String) {
|
||||||
|
val index: Int
|
||||||
|
val value: String
|
||||||
|
val lineLength = line.trim().length
|
||||||
|
if (lineLength <= 3) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val key : String = line.trim().substring(0, 1)
|
||||||
|
if (lineLength > 4 && line.trim().substring(3, 4) == ":") {
|
||||||
|
index = line.substring(1, 3).toIntOrNull() ?: 0
|
||||||
|
value = line.trim().substring(4)
|
||||||
|
} else {
|
||||||
|
index = line.substring(1, 2).toIntOrNull() ?: 0
|
||||||
|
value = line.trim().substring(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key != "") {
|
||||||
|
if ((key == "T") && (index == 0)) {
|
||||||
|
loadT(value)
|
||||||
|
} else if (key == "N") {
|
||||||
|
// Les lignes de type N seront parsées à la fin. preloadN() se chargera d'abord de réarranger la ligne
|
||||||
|
// pour bien gérer les parenthèses.
|
||||||
|
unparsedNote.add(index.toString()+value)
|
||||||
|
} else if (key == "M") {
|
||||||
|
val metaChunks: List<String> = value.split(REGEX_PARSE_META)
|
||||||
|
metaChunks.forEach { parseMeta(it) }
|
||||||
|
parseMeta(value)
|
||||||
|
sharedViewModel.setMeasure(meta["m"] ?: "")
|
||||||
|
sharedViewModel.setSongTitle(meta["t"] ?: "")
|
||||||
|
sharedViewModel.setSongAuthor(meta["a"] ?: "")
|
||||||
|
sharedViewModel.setSongComposer(meta["h"] ?: "")
|
||||||
|
sharedViewModel.setSongRhythm(meta["r"] ?: "")
|
||||||
|
} else if (key == "L") {
|
||||||
|
loadL(index, value)
|
||||||
|
} else if (key == "Y") {
|
||||||
|
loadY(index, value)
|
||||||
|
} else if (key == "E") {
|
||||||
|
loadE(index, value)
|
||||||
|
} else if (key == "U") {
|
||||||
|
loadU(value)
|
||||||
|
} else {
|
||||||
|
//setData(key, index, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private fun loadN(voiceNumber: Int, line: String) {
|
||||||
|
val newN = POneVoiceNote()
|
||||||
|
val lineRepeated = REGEX_REPETITION.replace(line) { matchResult ->
|
||||||
|
val (charIterated, iteration) = matchResult.destructured
|
||||||
|
val nTimes = iteration.toInt()
|
||||||
|
charIterated.repeat(nTimes)
|
||||||
|
}
|
||||||
|
val lineChar = lineRepeated.toCharArray()
|
||||||
|
lineChar.forEach {
|
||||||
|
when (it) {
|
||||||
|
'\'', ',' -> newN.appendLastNote(it)
|
||||||
|
'(', '[' -> newN.addNextMarker(it)
|
||||||
|
')', ']' -> newN.appendLastMarker(it)
|
||||||
|
'd', 'r', 'm', 'f', 's', 'l', 't',
|
||||||
|
'D', 'R', 'F', 'S', 'T',
|
||||||
|
'-', 'Z', 'z'
|
||||||
|
-> {
|
||||||
|
var noteString = it.toString().lowercase()
|
||||||
|
if (it == 'D' || it == 'R' || it == 'F' || it == 'S') {
|
||||||
|
noteString += "i"
|
||||||
|
}
|
||||||
|
if (it == 'T') {
|
||||||
|
noteString += "a"
|
||||||
|
}
|
||||||
|
newN.addItem(noteString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (voiceNumber == 1) {
|
||||||
|
N.add(0, POneVoiceNote())
|
||||||
|
}
|
||||||
|
N.add(voiceNumber, newN)
|
||||||
|
}
|
||||||
|
private fun loadT(line: String) {
|
||||||
|
templateString = line
|
||||||
|
var nextTemplate = ""
|
||||||
|
val nextMarker: MutableList<String> = mutableListOf()
|
||||||
|
var tMarker = ""
|
||||||
|
val lineChar = line.toCharArray()
|
||||||
|
lineChar.forEach {
|
||||||
|
if (tMarker != "") {
|
||||||
|
tMarker += it
|
||||||
|
if ((it == '}')
|
||||||
|
|| (tMarker.length == 2 && it != '{')) {
|
||||||
|
TimeUnitObject.hasMarker(true)
|
||||||
|
nextMarker.add(tMarker)
|
||||||
|
tMarker = ""
|
||||||
|
}
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
if (it == '$') {
|
||||||
|
tMarker = "$"
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
if (it == ':' || it == '|' || it == '/' || it == '!') {
|
||||||
|
val newTemplateItem =
|
||||||
|
PTemplate(nextTemplate, it.toString(), nextMarker.toMutableList())
|
||||||
|
T.add(newTemplateItem)
|
||||||
|
nextMarker.clear()
|
||||||
|
nextTemplate = ""
|
||||||
|
} else {
|
||||||
|
nextTemplate += it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val newTemplateItem = PTemplate(nextTemplate, "}", nextMarker)
|
||||||
|
T.add(newTemplateItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadU(line: String) {
|
||||||
|
val measureLong = meta.getOrElse("m") { "4/4" }
|
||||||
|
val measure = measureLong.split("/")[0].toIntOrNull() ?: 4
|
||||||
|
val uObject = ParseULine(line, measure)
|
||||||
|
val parsedULine = uObject.parsed()
|
||||||
|
loadT(parsedULine)
|
||||||
|
}
|
||||||
|
fun parseMeta(line: String) {
|
||||||
|
/* $_a_keyAbbrev = array(
|
||||||
|
'a' => 'author',
|
||||||
|
'c' => 'tonality',
|
||||||
|
'h' => 'composer',
|
||||||
|
'i' => 'interline',
|
||||||
|
'l' => 'lyrics font size',
|
||||||
|
'm' => 'rhythm',
|
||||||
|
'n' => 'note font size',
|
||||||
|
'r' => 'speed',
|
||||||
|
't' => 'title',
|
||||||
|
);*/
|
||||||
|
if (line.trim() == "") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val subKey: String = line.substring(0, 1).trim()
|
||||||
|
if (subKey == "|") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (subKey != "" && "M" != subKey) {
|
||||||
|
meta[subKey] = line.substring(2)
|
||||||
|
}
|
||||||
|
if ("c" == subKey) {
|
||||||
|
val tonality = line.uppercaseFirstMeta()
|
||||||
|
meta["C"] = tonality
|
||||||
|
sharedViewModel.setSongKey(tonality)
|
||||||
|
val keyOrigin = CommonTools.tonalityToNumber(tonality)
|
||||||
|
val transposeTo = getOpt("transposeto")
|
||||||
|
val transposeAsIf = getOpt("transposeasif")
|
||||||
|
val keyDest = if (transposeTo != "") {
|
||||||
|
CommonTools.tonalityToNumber(transposeTo)
|
||||||
|
} else {
|
||||||
|
keyOrigin
|
||||||
|
}
|
||||||
|
val keyAsIf = if (transposeAsIf != "") {
|
||||||
|
if (transposeAsIf.toIntOrNull() != null) {
|
||||||
|
keyOrigin + transposeAsIf.toInt()
|
||||||
|
} else {
|
||||||
|
CommonTools.tonalityToNumber(transposeAsIf)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keyOrigin
|
||||||
|
}
|
||||||
|
if (!meta.containsKey("transposeValue")) {
|
||||||
|
meta["transposeValue"] = (keyDest - keyAsIf).toString()
|
||||||
|
}
|
||||||
|
if (keyAsIf != keyOrigin) {
|
||||||
|
meta["C"] = CommonTools.numberToTonality(keyAsIf)
|
||||||
|
}
|
||||||
|
if (keyDest != keyOrigin) {
|
||||||
|
meta["C"] += " (" + CommonTools.numberToTonality(keyDest) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.uppercaseFirstMeta(): String {
|
||||||
|
if (this.length < 2) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return get(2).uppercaseChar() + substring(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.addHyphens(): String {
|
||||||
|
return this
|
||||||
|
.replace("_/", "/")
|
||||||
|
//.also { println ("1 $it")}
|
||||||
|
.replace("_", "-_")
|
||||||
|
//.also { println ("2 $it")}
|
||||||
|
.replace(Regex("_-(?=_)"), "_")
|
||||||
|
//.also { println ("3 $it")}
|
||||||
|
.replace(" -_", "_")
|
||||||
|
//.also { println ("4 $it")}
|
||||||
|
.replace(" _", "_")
|
||||||
|
//.also { println ("5 $it")}
|
||||||
|
.replace("--_", "-_")
|
||||||
|
//.also { println ("6 $it")}
|
||||||
|
.replace(Regex("_$"), "")
|
||||||
|
}
|
||||||
|
private fun loadL(stanzaNumber: Int, lyrics: String) {
|
||||||
|
try {
|
||||||
|
getLyricsComments(lyrics)
|
||||||
|
var loadedLyrics = lyrics.replace(REGEX_LYRICS_COMMENT, "")
|
||||||
|
.replace(REGEX_LYRICS_REPETITION) { matchResult ->
|
||||||
|
val repeating = matchResult.destructured.match.groupValues[1]
|
||||||
|
"_".repeat(repeating.toString().toInt())
|
||||||
|
}
|
||||||
|
//.replace("/", "_")
|
||||||
|
.addHyphens()
|
||||||
|
val arrayLyrics = loadedLyrics.split(Regex("[/_]"))
|
||||||
|
arrayLyrics.forEachIndexed { i, lyricsItem ->
|
||||||
|
addLyricsItem(stanzaNumber, i, lyricsItem)
|
||||||
|
}
|
||||||
|
if (lyricsComment.isNotEmpty()) {
|
||||||
|
val lyricsIterator = lyricsComment.iterator()
|
||||||
|
while (lyricsIterator.hasNext()) {
|
||||||
|
val item = lyricsIterator.next()
|
||||||
|
if (item.substring(0, 4) == "\${D:") {
|
||||||
|
item.replace(REGEX_LYRICS_COMMENT, "$1").addHyphens().split(Regex("[_/]")).drop(1).forEachIndexed { i, xval ->
|
||||||
|
appendLyricsItem(stanzaNumber, i, xval)
|
||||||
|
}
|
||||||
|
lyricsIterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
sharedViewModel.appendData("\nErr: $e")
|
||||||
|
}
|
||||||
|
if (stanzaNumber > sharedViewModel.nbStanzas.value) {
|
||||||
|
sharedViewModel.setNbStanzas(stanzaNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// loadY is a smart lyrics parser for Malagasy language
|
||||||
|
private fun loadY(intKey: Int, lyrics: String) {
|
||||||
|
val loadedLyrics = lyrics
|
||||||
|
.replace(REGEX_MALAGASY_MN_DASHED, "$1-\\\\$2")
|
||||||
|
.replace(REGEX_VOWELS_STAGE1, "$0_")
|
||||||
|
.replace(REGEX_VOWELS_STAGE2, "$1_")
|
||||||
|
.replace(" ", " _")
|
||||||
|
.replace("_\\ _", " ")
|
||||||
|
.replace("_\\", "")
|
||||||
|
.replace("_/", "/")
|
||||||
|
.replace("_0", "")
|
||||||
|
.replace(REGEX_MALAGASY_MN, "$1$2_$3")
|
||||||
|
.replace("_n'", "n'_")
|
||||||
|
getLyricsComments(loadedLyrics)
|
||||||
|
loadL(intKey, loadedLyrics.replace(REGEX_LYRICS_COMMENT, ""))
|
||||||
|
}
|
||||||
|
// loadE is a smart Lyrics parser for English language
|
||||||
|
private fun loadE(intKey: Int, lyrics: String) {
|
||||||
|
val loadedLyrics = lyrics
|
||||||
|
.replace(" ", " _")
|
||||||
|
.replace("\\ _", " ")
|
||||||
|
.replace("_\\", "")
|
||||||
|
.replace("_/", "/")
|
||||||
|
.replace("_0", "")
|
||||||
|
getLyricsComments(loadedLyrics)
|
||||||
|
loadL(intKey, loadedLyrics.replace(REGEX_LYRICS_COMMENT, ""))
|
||||||
|
}
|
||||||
|
private fun getLyricsComments(lyrics: String) {
|
||||||
|
val matchResult = REGEX_LYRICS_COMMENT.findAll(lyrics)
|
||||||
|
.map { it.value }.toList()
|
||||||
|
if (matchResult.isNotEmpty()) {
|
||||||
|
lyricsComment.addAll(matchResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun addLyricsItem(stanzaNumber: Int, i: Int, lyricsItem: String) {
|
||||||
|
while (L.size <= i) {
|
||||||
|
L.add(POneStanzaLyrics())
|
||||||
|
}
|
||||||
|
L[i].setLyrics(stanzaNumber, lyricsItem)
|
||||||
|
}
|
||||||
|
private fun appendLyricsItem(stanzaNumber: Int, i: Int, lyricsItem: String) {
|
||||||
|
if (L.size > i) {
|
||||||
|
L[i].appendDSLyrics(stanzaNumber, lyricsItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun getOpt(x: String) : String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
class TUNote (private val detailNote: MutableList<Note> = mutableListOf()){
|
||||||
|
var prevTUNote: TUNote? = null
|
||||||
|
var prevTUO: TimeUnitObject? = null
|
||||||
|
var annotations: AnnotatedTUO? = null
|
||||||
|
fun addNote(noteString: String) {
|
||||||
|
val newNote = Note(noteString)
|
||||||
|
detailNote.add(newNote)
|
||||||
|
}
|
||||||
|
fun from(note: Note?) {
|
||||||
|
detailNote.add(note ?: Note(""))
|
||||||
|
}
|
||||||
|
fun addMarker(newMarker: Char, markerBefore: String = "" ) {
|
||||||
|
if (newMarker == '0' && markerBefore == "") return
|
||||||
|
val lastNote = detailNote.lastOrNull()
|
||||||
|
if (lastNote == null) {
|
||||||
|
val newNote = Note("")
|
||||||
|
if (newMarker != '0') {
|
||||||
|
newNote.addMarker(newMarker.toString())
|
||||||
|
}
|
||||||
|
if (markerBefore != "") {
|
||||||
|
newNote.markBefore(markerBefore)
|
||||||
|
}
|
||||||
|
detailNote.add(newNote)
|
||||||
|
} else {
|
||||||
|
lastNote.addMarker(newMarker.toString())
|
||||||
|
if (markerBefore != "") {
|
||||||
|
lastNote.markBefore(markerBefore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun annotate(voiceNumber: Int) {
|
||||||
|
val annotatedTUO = AnnotatedTUO(this.toString(), voiceNumber)
|
||||||
|
annotations = if (annotatedTUO.underlineSpec.size > 0) {
|
||||||
|
annotatedTUO
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun toString() : String {
|
||||||
|
var result = detailNote.joinToString (separator = ""){ it.toString() }
|
||||||
|
.replace(Regex("\\. *,-$"), "")
|
||||||
|
.replace(Regex(",-$"), "")
|
||||||
|
.replace(Regex("(- *,z|- *,-|0$)"), "-")
|
||||||
|
.replace(Regex("([^\\.,])-"), "$1")
|
||||||
|
.replace(Regex("(?<=[drmfsltia]),,,"), "₃")
|
||||||
|
.replace(Regex("(?<=[drmfsltia]),,"), "₂")
|
||||||
|
.replace(Regex("(?<=[drmfsltia]),"), "₁")
|
||||||
|
.replace("'''", "³")
|
||||||
|
.replace("''", "²")
|
||||||
|
.replace("'", "¹")
|
||||||
|
.replace(Regex("[zZ][zZ\\.,]+"), "")
|
||||||
|
.replace(Regex("^-\\.,"), "")
|
||||||
|
.replace(Regex("\\.-$"), "")
|
||||||
|
.replace(Regex("\\((-[\\.,;][a-zA-Z][ia]*[¹²³₁₂₃]?)\\)"), "$1")
|
||||||
|
.replace("-", "―")
|
||||||
|
.replace(".", "\u2022")
|
||||||
|
.replace("z", "")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,391 @@
|
||||||
|
package mg.dot.feufaro.solfa
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.material.LocalTextStyle
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.draw.drawBehind
|
||||||
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.text.TextLayoutResult
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.TextUnit
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import mg.dot.feufaro.SharedViewModel
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
//import androidx.compose.ui.unit.times
|
||||||
|
|
||||||
|
class TimeUnitObject (val pTemplate: PTemplate, val prevTUO: TimeUnitObject?) {
|
||||||
|
var mutableNoteVersion: Int by mutableStateOf(0)
|
||||||
|
private var lyrics: MutableList<POneStanzaLyrics> = mutableListOf()
|
||||||
|
var sep0: String = ""
|
||||||
|
val tuNotes: MutableList<TUNote> = mutableListOf()
|
||||||
|
val numBlock: Int
|
||||||
|
var markerBefore: String = ""
|
||||||
|
private var annotated : Boolean
|
||||||
|
companion object {
|
||||||
|
var nbBlock: Int = 0
|
||||||
|
var sep1: String = ""
|
||||||
|
var _hasmarker: Boolean by mutableStateOf(false)
|
||||||
|
var nbStanzas: Int = 0
|
||||||
|
fun hasMarker(yes: Boolean) {
|
||||||
|
_hasmarker = yes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init {
|
||||||
|
nbBlock++
|
||||||
|
numBlock = nbBlock
|
||||||
|
sep0 = sep1
|
||||||
|
sep1 = pTemplate.separatorAfter
|
||||||
|
nbStanzas = 0
|
||||||
|
annotated = false
|
||||||
|
}
|
||||||
|
fun addLyrics(lyricsIn: POneStanzaLyrics) {
|
||||||
|
lyrics.add(lyricsIn)
|
||||||
|
}
|
||||||
|
private fun addNote(voiceNumber: Int, noteString: String, nextMarker: Char) {
|
||||||
|
val tuNote = tuNotes.getOrNull(voiceNumber)?.addNote(noteString)
|
||||||
|
if (tuNote == null) {
|
||||||
|
val prevTUNote: TUNote? = prevTUO?.tuNotes?.getOrNull(voiceNumber)
|
||||||
|
val newNote = TUNote()
|
||||||
|
newNote.prevTUNote = prevTUNote
|
||||||
|
newNote.prevTUO = prevTUO
|
||||||
|
newNote.addNote(noteString)
|
||||||
|
newNote.addMarker('0', markerBefore)
|
||||||
|
tuNotes.add(voiceNumber, newNote)
|
||||||
|
}
|
||||||
|
tuNotes[voiceNumber].addMarker(nextMarker)
|
||||||
|
}
|
||||||
|
fun addNote(voiceNumber: Int, oneVoiceNote: MutableList<PNotes>, nIndex: Int, nextMarker: Char) {
|
||||||
|
val thisNote: Note? = oneVoiceNote.getOrNull(nIndex)?.toNote()
|
||||||
|
val tuNote = tuNotes.getOrNull(voiceNumber)
|
||||||
|
if (tuNote == null) {
|
||||||
|
val newTUNote = newTUNote(voiceNumber, thisNote, nextMarker, markerBefore)
|
||||||
|
tuNotes.add(voiceNumber, newTUNote)
|
||||||
|
} else {
|
||||||
|
tuNote.from(thisNote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun newTUNote(voiceNumber: Int, thisNote: Note?, newMarker: Char, markerBefore: String): TUNote {
|
||||||
|
val prevTUNote: TUNote? = prevTUO?.tuNotes?.getOrNull(voiceNumber)
|
||||||
|
val newNote = TUNote()
|
||||||
|
newNote.prevTUNote = prevTUNote
|
||||||
|
newNote.prevTUO = prevTUO
|
||||||
|
newNote.from(thisNote)
|
||||||
|
if (newMarker != ' ') {
|
||||||
|
newNote.addMarker(newMarker, markerBefore)
|
||||||
|
}
|
||||||
|
return newNote
|
||||||
|
}
|
||||||
|
fun markBefore(newMarker: Char) {
|
||||||
|
markerBefore += newMarker
|
||||||
|
}
|
||||||
|
fun addMarker(newMarker: Char) {
|
||||||
|
if (tuNotes.size == 0) {
|
||||||
|
markBefore(newMarker)
|
||||||
|
}
|
||||||
|
tuNotes.forEach {
|
||||||
|
it.addMarker(newMarker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun addBlank(voiceNumber: Int, blank: Char) {
|
||||||
|
val blankString = if (blank == '-') "-" else ""
|
||||||
|
addNote(voiceNumber, blankString, '0')
|
||||||
|
}
|
||||||
|
fun getNum(): Int {
|
||||||
|
return numBlock
|
||||||
|
}
|
||||||
|
fun noteAsMultiString(): String {
|
||||||
|
val separatorBefore = when (sep0) {
|
||||||
|
":" -> sep0
|
||||||
|
"!" -> "|"
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
val markersBefore = ""//pTemplate.template.replace(Regex("[a-yA-Y\\(\\[].*"), "")
|
||||||
|
return tuNotes.drop(1).joinToString (separator = "\n"){
|
||||||
|
separatorBefore + markersBefore + it.toString().replace(Regex("[\\(\\)\\[\\]]"), "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun annotate() {
|
||||||
|
// We must annotate once, because, annotations can be called asynchronously after first filling
|
||||||
|
if (!annotated) {
|
||||||
|
tuNotes.forEachIndexed { voiceNumber, tuNote ->
|
||||||
|
tuNote.annotate(voiceNumber)
|
||||||
|
}
|
||||||
|
annotated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun annotations(): List<AnnotatedTUO> {
|
||||||
|
val xreturn = tuNotes.mapNotNull {
|
||||||
|
it.annotations
|
||||||
|
}
|
||||||
|
return xreturn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lyricsAsMultiString(stanzaNumber: Int): String {
|
||||||
|
var result: MutableList<String> = emptyList<String>().toMutableList()
|
||||||
|
lyrics.forEach {
|
||||||
|
val byLines = it.toList(stanzaNumber)
|
||||||
|
byLines.forEachIndexed { i, v ->
|
||||||
|
while(result.size <= i) {
|
||||||
|
result.add(i, "")
|
||||||
|
}
|
||||||
|
result[i] += "\u00A0$v"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.joinToString(separator = "\n") { it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TimeUnitComposable(
|
||||||
|
tuo: TimeUnitObject,
|
||||||
|
stanzaNumber: Int
|
||||||
|
) {
|
||||||
|
val col = if (tuo.getNum() % 2 == 0) Color(0xff, 0xfa, 0xf7) else Color(0xfb, 0xf3, 0xff)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(col)
|
||||||
|
) {
|
||||||
|
if (TimeUnitObject._hasmarker) {
|
||||||
|
AutoResizingText(
|
||||||
|
text = tuo.pTemplate.markerToString(),
|
||||||
|
minFontSize = 8.sp,
|
||||||
|
maxFontSize = 18.sp,
|
||||||
|
maxLines = 1,
|
||||||
|
modifier = Modifier.fillMaxWidth())
|
||||||
|
}
|
||||||
|
Row (modifier = Modifier.height(IntrinsicSize.Min)){
|
||||||
|
if (tuo.sep0 == "/" || tuo.sep0 == "|") {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(1.dp)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.background(Color.Black)
|
||||||
|
)
|
||||||
|
if (tuo.sep0 == "/") {
|
||||||
|
Box(modifier = Modifier.width(2.dp))
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(1.dp)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.background(Color.Black)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(4.dp)
|
||||||
|
.fillMaxHeight()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
tuo.annotate()
|
||||||
|
// Utile pour que Compose recalcule la cellule. La valeur sera toutefois toujours égale à ""
|
||||||
|
val mutableNoteVersionX = if (tuo.mutableNoteVersion == -1) "7" else ""
|
||||||
|
val multiLineText = tuo.noteAsMultiString()
|
||||||
|
var textLayoutResult: TextLayoutResult? by remember { mutableStateOf(null) }
|
||||||
|
Box (modifier = Modifier.fillMaxWidth()
|
||||||
|
.drawBehind {
|
||||||
|
tuo.annotations().mapNotNull { ta ->
|
||||||
|
ta.underlineSpec.mapNotNull { us ->
|
||||||
|
var xStart = 0f
|
||||||
|
val separatorLength = if (tuo.sep0 in listOf(":", "!")) 1 else 0
|
||||||
|
if (us.x > -1) {
|
||||||
|
val lineGlobalStartOffset = textLayoutResult?.getLineStart(ta.voiceNumber - 1) ?: 0
|
||||||
|
val globalStartIndex = lineGlobalStartOffset + us.x + separatorLength
|
||||||
|
xStart = textLayoutResult?.getCursorRect(globalStartIndex)?.left ?: 0f
|
||||||
|
}
|
||||||
|
var xEnd = size.width
|
||||||
|
if (us.y > -1 ) {
|
||||||
|
val lineGlobalStartOffset = textLayoutResult?.getLineStart(ta.voiceNumber - 1) ?: 0
|
||||||
|
// don't ask me why + 1 here makes it work, but it works.
|
||||||
|
val globalEndIndex = min(lineGlobalStartOffset + us.y + separatorLength + 1, multiLineText.length)
|
||||||
|
|
||||||
|
xEnd = textLayoutResult?.getCursorRect(globalEndIndex)?.right ?: size.width
|
||||||
|
}
|
||||||
|
//if (it.underlineSpec.size > 0) {
|
||||||
|
var colorUnderline = when (tuo.mutableNoteVersion) {
|
||||||
|
0 -> Color.Red
|
||||||
|
1 -> Color.Yellow
|
||||||
|
2 -> Color.Magenta
|
||||||
|
3 -> Color.Blue
|
||||||
|
4 -> Color.Green
|
||||||
|
else -> Color.Black
|
||||||
|
}
|
||||||
|
val totalHeight = textLayoutResult?.size?.height ?: 0
|
||||||
|
val nbNotes = (multiLineText + mutableNoteVersionX).split("\n").size
|
||||||
|
val yPos = ta.voiceNumber * totalHeight.toFloat() / nbNotes
|
||||||
|
drawLine(
|
||||||
|
colorUnderline,
|
||||||
|
start = Offset(xStart, yPos),
|
||||||
|
end = Offset(xEnd, yPos)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
){
|
||||||
|
Text(
|
||||||
|
text = multiLineText + mutableNoteVersionX,
|
||||||
|
onTextLayout = { result ->
|
||||||
|
textLayoutResult = result
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AutoResizingText(
|
||||||
|
text = tuo.lyricsAsMultiString(stanzaNumber),
|
||||||
|
minFontSize = 8.sp,
|
||||||
|
maxFontSize = 16.sp,
|
||||||
|
maxLines = 1,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun bestTUOWidth(items: List<TimeUnitObject>): Dp {
|
||||||
|
val textMeasurer = rememberTextMeasurer()
|
||||||
|
val density = LocalDensity.current
|
||||||
|
var maxWidth by remember { mutableStateOf(0.dp) }
|
||||||
|
LaunchedEffect(items) {
|
||||||
|
maxWidth = 0.dp
|
||||||
|
items.forEach {
|
||||||
|
val textLayoutResult: TextLayoutResult = textMeasurer.measure(
|
||||||
|
text = it.noteAsMultiString(),
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
val textWidth = with(density) {
|
||||||
|
textLayoutResult.size.width.toDp()
|
||||||
|
}
|
||||||
|
if (textWidth > maxWidth) {
|
||||||
|
maxWidth = textWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxWidth + 13.dp
|
||||||
|
}
|
||||||
|
@Composable
|
||||||
|
fun AutoResizingText(
|
||||||
|
text: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
maxFontSize: TextUnit = 24.sp, // Taille de police maximale de référence
|
||||||
|
minFontSize: TextUnit = 10.sp, // Taille de police minimale autorisée
|
||||||
|
textStyle: TextStyle = LocalTextStyle.current,
|
||||||
|
maxLines: Int = 1 // Pour s'assurer que le texte ne déborde jamais sur deux lignes
|
||||||
|
) {
|
||||||
|
var fontSize by remember { mutableStateOf(maxFontSize) }
|
||||||
|
val textMeasurer = rememberTextMeasurer() // Mesure le texte hors de la composition
|
||||||
|
val density = LocalDensity.current
|
||||||
|
|
||||||
|
BoxWithConstraints(modifier = modifier) {
|
||||||
|
// La largeur disponible pour le texte
|
||||||
|
val maxWidthPx = with(density) { maxWidth.toPx() }
|
||||||
|
|
||||||
|
// Un Text() invisible ou non affiché pour mesurer le texte sans le dessiner directement
|
||||||
|
// Vous pouvez aussi utiliser textMeasurer.measure() directement
|
||||||
|
// On rend le texte visible une fois la taille ajustée pour éviter les clignotements
|
||||||
|
var readyToDraw by remember { mutableStateOf(false) }
|
||||||
|
val textLinesCount = text.split("\n").size * maxLines
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
// Utiliser le même style que le Text final, mais ajuster la taille
|
||||||
|
style = textStyle.copy(fontSize = fontSize),
|
||||||
|
maxLines = textLinesCount, // Très important pour qu'il ne déborde pas en hauteur
|
||||||
|
overflow = TextOverflow.Clip, // Masque si ça déborde encore (ne devrait pas si le calcul est bon)
|
||||||
|
// onTextLayout est appelé après que le texte ait été mesuré
|
||||||
|
onTextLayout = { textLayoutResult: TextLayoutResult ->
|
||||||
|
if (textLayoutResult.hasVisualOverflow && fontSize > minFontSize) {
|
||||||
|
// Si le texte déborde en largeur, réduisez la taille de la police
|
||||||
|
// Vous pouvez utiliser un pas plus fin ou une méthode de recherche binaire pour optimiser
|
||||||
|
fontSize *= 0.9f // Réduire de 10% à chaque fois
|
||||||
|
} else {
|
||||||
|
readyToDraw = true // La taille est stable ou min atteinte, on peut dessiner
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Le modificateur drawWithContent est utilisé pour retarder le dessin
|
||||||
|
// jusqu'à ce que la taille de police finale soit déterminée.
|
||||||
|
modifier = Modifier.fillMaxWidth() // Le modifier du Text interne peut être ajusté
|
||||||
|
.drawWithContent {
|
||||||
|
if (readyToDraw) {
|
||||||
|
drawContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Composable
|
||||||
|
fun LazyVerticalGridTUO(
|
||||||
|
viewModel: SharedViewModel,
|
||||||
|
gridWidthPx: Int,
|
||||||
|
onGridWidthMeasured: (Int) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val regexMeasure = Regex("(\\d)/\\d").find(viewModel.measure.value)
|
||||||
|
val tuoList by viewModel.tuoList.collectAsState()
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.onSizeChanged { size ->
|
||||||
|
onGridWidthMeasured(size.width)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
val density = LocalDensity.current
|
||||||
|
val horizontalArrangementSpacing: Dp = 0.dp
|
||||||
|
val columnGroupString = regexMeasure?.groupValues?.get(1) ?: "1"
|
||||||
|
val columnGroup = columnGroupString.toInt().coerceAtLeast(1)
|
||||||
|
val itemMinBaseWidth = bestTUOWidth(tuoList)
|
||||||
|
val gridWidthDp = with(density) { gridWidthPx.toDp() }
|
||||||
|
|
||||||
|
val actualColumns = remember(gridWidthDp, itemMinBaseWidth, columnGroup) {
|
||||||
|
if (gridWidthDp == 0.dp) return@remember GridCells.Fixed(columnGroup)
|
||||||
|
var calculatedCols =
|
||||||
|
(gridWidthDp / (itemMinBaseWidth + horizontalArrangementSpacing)).toInt()
|
||||||
|
if (calculatedCols == 0) calculatedCols = columnGroup
|
||||||
|
if (calculatedCols % columnGroup != 0) {
|
||||||
|
calculatedCols = (calculatedCols / columnGroup) * columnGroup
|
||||||
|
if (calculatedCols == 0) calculatedCols = columnGroup
|
||||||
|
}
|
||||||
|
GridCells.Fixed(calculatedCols.coerceAtLeast(columnGroup))
|
||||||
|
|
||||||
|
}
|
||||||
|
val currentStanza by viewModel.stanza.collectAsState()
|
||||||
|
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = actualColumns,
|
||||||
|
modifier = Modifier.fillMaxWidth().heightIn(max = 800.dp),
|
||||||
|
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(horizontalArrangementSpacing),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
contentPadding = PaddingValues(8.dp) // c'est juste le padding exterieur de toute la liste
|
||||||
|
) {
|
||||||
|
items(items = tuoList.drop(1), key = { it.numBlock }) { oneTUO ->
|
||||||
|
TimeUnitComposable(
|
||||||
|
tuo = oneTUO,
|
||||||
|
currentStanza
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -24,7 +24,10 @@ class DesktopFileRepository : FileRepository { // IMPORTS AND IMPLEMENTS THE com
|
||||||
throw IOException("Failed to read file or asset '$filePath'")
|
throw IOException("Failed to read file or asset '$filePath'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override suspend fun readFileContent(filePath: String): String = withContext(Dispatchers.IO) { "" }
|
override suspend fun readFileContent(filePath: String): String = withContext(Dispatchers.IO) {
|
||||||
|
val lines = readFileLines(filePath)
|
||||||
|
lines.joinToString("\n") { it }
|
||||||
|
}
|
||||||
private fun readAssetFileLines(assetFileName: String): List<String> {
|
private fun readAssetFileLines(assetFileName: String): List<String> {
|
||||||
return try {
|
return try {
|
||||||
//myApplicationContext.assets.open(filename.removePrefix("assets://")).bufferedReader().use {
|
//myApplicationContext.assets.open(filename.removePrefix("assets://")).bufferedReader().use {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ import androidx.compose.ui.window.application
|
||||||
import mg.dot.feufaro.di.commonModule
|
import mg.dot.feufaro.di.commonModule
|
||||||
import mg.dot.feufaro.di.desktopModule
|
import mg.dot.feufaro.di.desktopModule
|
||||||
import org.koin.core.context.GlobalContext.startKoin
|
import org.koin.core.context.GlobalContext.startKoin
|
||||||
|
import org.koin.core.context.KoinContext
|
||||||
import org.koin.core.logger.Level
|
import org.koin.core.logger.Level
|
||||||
|
import org.koin.compose.KoinContext
|
||||||
|
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
startKoin {
|
startKoin {
|
||||||
|
|
@ -21,6 +23,8 @@ fun main() = application {
|
||||||
onCloseRequest = ::exitApplication,
|
onCloseRequest = ::exitApplication,
|
||||||
title = "Feufaro",
|
title = "Feufaro",
|
||||||
) {
|
) {
|
||||||
|
KoinContext {
|
||||||
App()
|
App()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -17,6 +17,9 @@ kotlin = "2.2.0"
|
||||||
kotlinx-coroutines = "1.10.2"
|
kotlinx-coroutines = "1.10.2"
|
||||||
#20250704
|
#20250704
|
||||||
koin = "4.0.4"
|
koin = "4.0.4"
|
||||||
|
core = "0.91.1"
|
||||||
|
kmpObservableviewmodelCore = "1.0.0-BETA-3"
|
||||||
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||||
|
|
@ -36,6 +39,12 @@ koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
|
||||||
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
|
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" }
|
||||||
koin-core-viewmodel = { module = "io.insert-koin:koin-core-viewmodel", version.ref = "koin" }
|
koin-core-viewmodel = { module = "io.insert-koin:koin-core-viewmodel", version.ref = "koin" }
|
||||||
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
|
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
|
||||||
|
#20250704
|
||||||
|
core = { module = "io.github.pdvrieze.xmlutil:core", version.ref = "core" }
|
||||||
|
#core-android = { module = "io.github.pdvrieze.xmlutil:core-android", version.ref = "core" }
|
||||||
|
#core-jdk = { module = "io.github.pdvrieze.xmlutil:core-jdk", version.ref = "core" }
|
||||||
|
serialization = { module = "io.github.pdvrieze.xmlutil:serialization", version.ref = "core" }
|
||||||
|
kmp-observableviewmodel-core = { module = "com.rickclephas.kmp:kmp-observableviewmodel-core", version.ref = "kmpObservableviewmodelCore" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue