diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiPitch.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiPitch.kt index 346f89d..384dd17 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiPitch.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiPitch.kt @@ -1,5 +1,6 @@ package mg.dot.feufaro.midi +import mg.dot.feufaro.solfa.ParseULine import mg.dot.feufaro.solfa.Transpose @@ -10,7 +11,10 @@ data class MidiPitch ( var alter: String = "", var duration: Int = 0, var markers: List = listOf(), - var tick : Int = 0 + var tick : Int = 0, + var metaBytes: String = "", + var metaByteSize: Int = 0, + var metaType : Int = -1 ) { var velocity: Int = 96 companion object { @@ -18,18 +22,25 @@ data class MidiPitch ( var curVoiceNumber: Int = 0 var nextTick : MutableList = mutableListOf() } + fun setMeta(type: Int, tickMeta: Int, nb: Int, data0: Int, data1: Int = 0, data2: Int = 0, data3: Int = 0, data4: Int = 0) { + metaBytes = ""+ data0.toChar() + data1.toChar() + data2.toChar() + data3.toChar() + data4.toChar() + tick = tickMeta + metaByteSize = nb + metaType = type + } fun setNote(note: String, theDuration: Int) { val midiPitch = Transpose.transposeToMidi(note, key, "C") - if (midiPitch < 0) { - velocity = 0 + velocity = if (midiPitch < 0) { + 0 } else { - velocity = 99 + 99 } pitch = midiPitch.toString() duration = theDuration - tick = nextTick[voiceNumber] + tick = nextTick.getOrNull(voiceNumber) ?: 0 nextTick[voiceNumber] = theDuration + tick voiceNumber = curVoiceNumber + metaType = -1 } fun setMarker(theMarkers : MutableList) { if (theMarkers.isNotEmpty()) { @@ -46,6 +57,27 @@ data class MidiPitch ( } fun initKey(theKey: String) { key = theKey + val armature = Transpose.toArmature(key) + val tickMeta = nextTick.getOrNull(0) ?: 0 + setMeta(0x59, tickMeta, 2, armature) + } + fun initMeasure(theMeasure: String): Boolean { + val numerator = theMeasure.replace(Regex("/.*"), "").toIntOrNull() + val denominator = theMeasure.replace(Regex("^[^/]*/(\\d+).*$"), "$1").toIntOrNull() ?: 4 + val data1 = when (denominator) { + 1 -> 0 + 2 -> 1 + 4 -> 2 + 8 -> 3 + 16 -> 4 + else -> 5 + } + val blankDuration = (4 * (numerator ?: 4) - ParseULine.blankDuration) * 15 + if (numerator != null) { + setMeta(0x58, blankDuration, 4, numerator, data1, 60, 20) + return true + } + return false } fun reset() { tick = 0 diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiWriterKotlin.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiWriterKotlin.kt index 2d166cf..9db3982 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiWriterKotlin.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/midi/MidiWriterKotlin.kt @@ -14,13 +14,18 @@ class MidiWriterKotlin (private val fileRepository: FileRepository) { private val noteOn = ShortMessage() private val noteOff = ShortMessage() private val lastPitch : MutableList = mutableListOf() + private val useChord : Boolean = true fun addNote( voiceNumber: Int, note: Int, velocity: Int, tick: Long) { + var channel: Int = voiceNumber - 1 + if (useChord) { + channel = channel / 2 + } var note = note if (voiceNumber == 3 || voiceNumber == 4) { note -= 12 } if (lastPitch.size > voiceNumber && lastPitch[voiceNumber] > 0) { - noteOff.setMessage(ShortMessage.NOTE_OFF, voiceNumber - 1, lastPitch[voiceNumber], 0) + noteOff.setMessage(ShortMessage.NOTE_OFF, channel, lastPitch[voiceNumber], 0) val n2 = noteOff.clone() as MidiMessage track.add(MidiEvent(n2, tick)) } @@ -29,7 +34,7 @@ class MidiWriterKotlin (private val fileRepository: FileRepository) { note = 40 velocity = 0 } - noteOn.setMessage(ShortMessage.NOTE_ON, voiceNumber - 1, note, velocity) + noteOn.setMessage(ShortMessage.NOTE_ON, channel, note, velocity) val n1: MidiMessage = noteOn.clone() as MidiMessage track.add(MidiEvent(n1, tick)) while(lastPitch.size <= voiceNumber) { @@ -45,12 +50,22 @@ class MidiWriterKotlin (private val fileRepository: FileRepository) { out.close() } } + fun addMetaMessage(type: Int, tick: Int, nbData: Int, metaByteString: String) { + val byteArray = metaByteString.toByteArray() + val metaMessage = MetaMessage(type, byteArray, nbData) + track.add(MidiEvent(metaMessage, tick.toLong())) + } fun process(pitches: List) { val lastTick = 0 nextTick.clear() + // addMetaMessage(0x59, 4, 2, 2,0) tick = 0 pitches.forEach { - addNote(it.voiceNumber, it.pitch.toInt(), 100, it.tick.toLong()) + if (it.metaType > 0) { + addMetaMessage(it.metaType, it.tick, it.metaByteSize, it.metaBytes) + } else if (it.pitch != "") { + addNote(it.voiceNumber, it.pitch.toInt(), 100, it.tick.toLong()) + } } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/musicXML/MusicXML.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/musicXML/MusicXML.kt index 752d9cf..60f5092 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/musicXML/MusicXML.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/musicXML/MusicXML.kt @@ -80,7 +80,7 @@ class MusicXML(private val fileRepository: FileRepository) { } } suspend fun load() { - val xmlContent = fileRepository.readFileContent("assets://31.xml") + val xmlContent = fileRepository.readFileContent("assets://ews-89.xml") val xslContent = fileRepository.readFileContent("assets://timepart.xsl") xmlString = performXsltTransformation(xmlContent, xslContent) val solfaXML = SolfaXML() @@ -215,7 +215,7 @@ class MusicXML(private val fileRepository: FileRepository) { init { val parseScope = CoroutineScope(Dispatchers.Default) parseScope.launch { - if (true) + if (false) load() } } diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/ParseULine.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/ParseULine.kt index 186fec4..bd3147f 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/ParseULine.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/ParseULine.kt @@ -15,6 +15,7 @@ class ParseULine (var line: String, var measure: Int) { 'y' to listOf(".,D:", ".-,:D,", ":,D.", ":-,.D,"), 't' to listOf("DD", "DD", "DD", "DD") ) + var blankDuration: Int = 0 } fun parsed() : String { @@ -28,6 +29,7 @@ class ParseULine (var line: String, var measure: Int) { "O" -> 24 else -> capturedChar.toInt() } + blankDuration = cursorX line = matchResult.groups[2]!!.value parsedString = ":" if (capturedChar == "0") { diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt index a0c61f7..d0a6bfc 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt @@ -166,9 +166,9 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository println("Erreur parseScope Solfa:150 : ${e.message} iter: ${TimeUnitObject.nbBlock}") } val pitches = pitches.sortedWith( compareBy({it.tick}, {it.voiceNumber})) - val k = MidiWriterKotlin(fileRepository) - k.process(pitches) - k.save("whawyd3.mid") + val midiWriter = MidiWriterKotlin(fileRepository) + midiWriter.process(pitches) + midiWriter.save("whawyd3.mid") } } @@ -363,12 +363,51 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository sharedScreenModel.setSongRhythm(meta["r"] ?: "") } private fun loadN(voiceNumber: Int, line: String, templateComments: MutableList) { - val midiPitch = MidiPitch(voiceNumber) - midiPitch.currentVoiceNumber(voiceNumber) - midiPitch.initKey(meta["C"] ?: "C") var midiDuration = 0 var nextDuration = 0 var lastNoteString = "" + val midiPitch = MidiPitch(voiceNumber) + var nextComment: MutableList = mutableListOf() + fun pushMidi(typeBlock: String = "note") { + if (typeBlock == "measure") { + val z = midiPitch.copy() + if (z.tick != 0) { + val tickMeta = midiPitch.tick + val numerator = midiPitch.metaBytes.toByteArray()[0].toInt() + val denominatorPower = midiPitch.metaBytes.toByteArray()[1].toInt() + val denominator = 1 shl denominatorPower + z.tick = 0 + val newNumerator = tickMeta / 15 / denominator + z.metaBytes = "" + newNumerator.toChar() + denominatorPower.toChar() + 60.toChar() + 20.toChar() + + pitches.add(z) + } + return; + } + if (midiDuration > 0 && typeBlock == "note") { + midiPitch.setNote(lastNoteString, midiDuration) + pitches.add(midiPitch.copy()) + midiPitch.setMarker(nextComment) + } else if (typeBlock == "meta") { + pitches.add(midiPitch.copy()) + } + midiPitch.metaBytes = "" + midiPitch.metaType = -1 + midiPitch.duration = 0 + lastNoteString = "" + midiDuration = 0 + nextComment = mutableListOf() + } + + midiPitch.currentVoiceNumber(voiceNumber) + if (voiceNumber == 1) { + midiPitch.initKey(meta["C"] ?: "C") + pushMidi("meta") + if (midiPitch.initMeasure(meta["m"] ?: "4/4")) { + pushMidi("measure") + pushMidi("meta") + } + } var lastIt = ' ' val newN = POneVoiceNote() val lineRepeated = REGEX_REPETITION.replace(line) { matchResult -> @@ -377,17 +416,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository charIterated.repeat(nTimes) } var commentNumber = -1 - var nextComment: MutableList = mutableListOf() - fun pushMidi() { - if (midiDuration > 0) { - midiPitch.setNote(lastNoteString, midiDuration) - pitches.add(midiPitch.copy()) - midiPitch.setMarker(nextComment) - } - lastNoteString = "" - midiDuration = 0 - nextComment = mutableListOf() - } val lineChar = lineRepeated.toCharArray() lineChar.forEach { when (it) { diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Transpose.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Transpose.kt index 8314867..e59856e 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Transpose.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Transpose.kt @@ -24,6 +24,7 @@ class Transpose { val octaveSigns = listOf("₄", "₃", "₂", "₁", "", "¹", "²", "³", "⁴") val noteToNumber = listOf("d", "di", "r", "ri", "m", "f", "fi", "s", "si", "l", "ta", "t") val keyToNumber = listOf("C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" ) + val keyToArmature = listOf(0, -7, 2, -3, 4, -1, -6, 1, -4, 3, -2, 5 ) fun compareNotes(note1: String, note2: String): Int { val found1 = REGEX_ONE_NOTE.find(note1) val found2 = REGEX_ONE_NOTE.find(note2) @@ -46,6 +47,10 @@ class Transpose { } } } + fun toArmature(note: String): Int { + val index = keyToNumber.indexOf(note) + return keyToArmature.getOrNull(index) ?: 0 + } fun transposeMidi(note: String, fromKey: String) : Pair { return Pair(note, 4) }