Midi generation enhancements
This commit is contained in:
parent
a35d7c38ff
commit
3867dbc855
6 changed files with 109 additions and 27 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
package mg.dot.feufaro.midi
|
package mg.dot.feufaro.midi
|
||||||
|
|
||||||
|
import mg.dot.feufaro.solfa.ParseULine
|
||||||
import mg.dot.feufaro.solfa.Transpose
|
import mg.dot.feufaro.solfa.Transpose
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,7 +11,10 @@ data class MidiPitch (
|
||||||
var alter: String = "",
|
var alter: String = "",
|
||||||
var duration: Int = 0,
|
var duration: Int = 0,
|
||||||
var markers: List<String> = listOf(),
|
var markers: List<String> = listOf(),
|
||||||
var tick : Int = 0
|
var tick : Int = 0,
|
||||||
|
var metaBytes: String = "",
|
||||||
|
var metaByteSize: Int = 0,
|
||||||
|
var metaType : Int = -1
|
||||||
) {
|
) {
|
||||||
var velocity: Int = 96
|
var velocity: Int = 96
|
||||||
companion object {
|
companion object {
|
||||||
|
|
@ -18,18 +22,25 @@ data class MidiPitch (
|
||||||
var curVoiceNumber: Int = 0
|
var curVoiceNumber: Int = 0
|
||||||
var nextTick : MutableList<Int> = mutableListOf()
|
var nextTick : MutableList<Int> = 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) {
|
fun setNote(note: String, theDuration: Int) {
|
||||||
val midiPitch = Transpose.transposeToMidi(note, key, "C")
|
val midiPitch = Transpose.transposeToMidi(note, key, "C")
|
||||||
if (midiPitch < 0) {
|
velocity = if (midiPitch < 0) {
|
||||||
velocity = 0
|
0
|
||||||
} else {
|
} else {
|
||||||
velocity = 99
|
99
|
||||||
}
|
}
|
||||||
pitch = midiPitch.toString()
|
pitch = midiPitch.toString()
|
||||||
duration = theDuration
|
duration = theDuration
|
||||||
tick = nextTick[voiceNumber]
|
tick = nextTick.getOrNull(voiceNumber) ?: 0
|
||||||
nextTick[voiceNumber] = theDuration + tick
|
nextTick[voiceNumber] = theDuration + tick
|
||||||
voiceNumber = curVoiceNumber
|
voiceNumber = curVoiceNumber
|
||||||
|
metaType = -1
|
||||||
}
|
}
|
||||||
fun setMarker(theMarkers : MutableList<String>) {
|
fun setMarker(theMarkers : MutableList<String>) {
|
||||||
if (theMarkers.isNotEmpty()) {
|
if (theMarkers.isNotEmpty()) {
|
||||||
|
|
@ -46,6 +57,27 @@ data class MidiPitch (
|
||||||
}
|
}
|
||||||
fun initKey(theKey: String) {
|
fun initKey(theKey: String) {
|
||||||
key = theKey
|
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() {
|
fun reset() {
|
||||||
tick = 0
|
tick = 0
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,18 @@ class MidiWriterKotlin (private val fileRepository: FileRepository) {
|
||||||
private val noteOn = ShortMessage()
|
private val noteOn = ShortMessage()
|
||||||
private val noteOff = ShortMessage()
|
private val noteOff = ShortMessage()
|
||||||
private val lastPitch : MutableList<Int> = mutableListOf()
|
private val lastPitch : MutableList<Int> = mutableListOf()
|
||||||
|
private val useChord : Boolean = true
|
||||||
fun addNote( voiceNumber: Int, note: Int, velocity: Int, tick: Long) {
|
fun addNote( voiceNumber: Int, note: Int, velocity: Int, tick: Long) {
|
||||||
|
var channel: Int = voiceNumber - 1
|
||||||
|
if (useChord) {
|
||||||
|
channel = channel / 2
|
||||||
|
}
|
||||||
var note = note
|
var note = note
|
||||||
if (voiceNumber == 3 || voiceNumber == 4) {
|
if (voiceNumber == 3 || voiceNumber == 4) {
|
||||||
note -= 12
|
note -= 12
|
||||||
}
|
}
|
||||||
if (lastPitch.size > voiceNumber && lastPitch[voiceNumber] > 0) {
|
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
|
val n2 = noteOff.clone() as MidiMessage
|
||||||
track.add(MidiEvent(n2, tick))
|
track.add(MidiEvent(n2, tick))
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +34,7 @@ class MidiWriterKotlin (private val fileRepository: FileRepository) {
|
||||||
note = 40
|
note = 40
|
||||||
velocity = 0
|
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
|
val n1: MidiMessage = noteOn.clone() as MidiMessage
|
||||||
track.add(MidiEvent(n1, tick))
|
track.add(MidiEvent(n1, tick))
|
||||||
while(lastPitch.size <= voiceNumber) {
|
while(lastPitch.size <= voiceNumber) {
|
||||||
|
|
@ -45,12 +50,22 @@ class MidiWriterKotlin (private val fileRepository: FileRepository) {
|
||||||
out.close()
|
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<MidiPitch>) {
|
fun process(pitches: List<MidiPitch>) {
|
||||||
val lastTick = 0
|
val lastTick = 0
|
||||||
nextTick.clear()
|
nextTick.clear()
|
||||||
|
// addMetaMessage(0x59, 4, 2, 2,0)
|
||||||
tick = 0
|
tick = 0
|
||||||
pitches.forEach {
|
pitches.forEach {
|
||||||
|
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())
|
addNote(it.voiceNumber, it.pitch.toInt(), 100, it.tick.toLong())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +80,7 @@ class MusicXML(private val fileRepository: FileRepository) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
suspend fun load() {
|
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")
|
val xslContent = fileRepository.readFileContent("assets://timepart.xsl")
|
||||||
xmlString = performXsltTransformation(xmlContent, xslContent)
|
xmlString = performXsltTransformation(xmlContent, xslContent)
|
||||||
val solfaXML = SolfaXML()
|
val solfaXML = SolfaXML()
|
||||||
|
|
@ -215,7 +215,7 @@ class MusicXML(private val fileRepository: FileRepository) {
|
||||||
init {
|
init {
|
||||||
val parseScope = CoroutineScope(Dispatchers.Default)
|
val parseScope = CoroutineScope(Dispatchers.Default)
|
||||||
parseScope.launch {
|
parseScope.launch {
|
||||||
if (true)
|
if (false)
|
||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class ParseULine (var line: String, var measure: Int) {
|
||||||
'y' to listOf(".,D:", ".-,:D,", ":,D.", ":-,.D,"),
|
'y' to listOf(".,D:", ".-,:D,", ":,D.", ":-,.D,"),
|
||||||
't' to listOf("DD", "DD", "DD", "DD")
|
't' to listOf("DD", "DD", "DD", "DD")
|
||||||
)
|
)
|
||||||
|
var blankDuration: Int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parsed() : String {
|
fun parsed() : String {
|
||||||
|
|
@ -28,6 +29,7 @@ class ParseULine (var line: String, var measure: Int) {
|
||||||
"O" -> 24
|
"O" -> 24
|
||||||
else -> capturedChar.toInt()
|
else -> capturedChar.toInt()
|
||||||
}
|
}
|
||||||
|
blankDuration = cursorX
|
||||||
line = matchResult.groups[2]!!.value
|
line = matchResult.groups[2]!!.value
|
||||||
parsedString = ":"
|
parsedString = ":"
|
||||||
if (capturedChar == "0") {
|
if (capturedChar == "0") {
|
||||||
|
|
|
||||||
|
|
@ -166,9 +166,9 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
println("Erreur parseScope Solfa:150 : ${e.message} iter: ${TimeUnitObject.nbBlock}")
|
println("Erreur parseScope Solfa:150 : ${e.message} iter: ${TimeUnitObject.nbBlock}")
|
||||||
}
|
}
|
||||||
val pitches = pitches.sortedWith( compareBy({it.tick}, {it.voiceNumber}))
|
val pitches = pitches.sortedWith( compareBy({it.tick}, {it.voiceNumber}))
|
||||||
val k = MidiWriterKotlin(fileRepository)
|
val midiWriter = MidiWriterKotlin(fileRepository)
|
||||||
k.process(pitches)
|
midiWriter.process(pitches)
|
||||||
k.save("whawyd3.mid")
|
midiWriter.save("whawyd3.mid")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -363,12 +363,51 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
sharedScreenModel.setSongRhythm(meta["r"] ?: "")
|
sharedScreenModel.setSongRhythm(meta["r"] ?: "")
|
||||||
}
|
}
|
||||||
private fun loadN(voiceNumber: Int, line: String, templateComments: MutableList<String>) {
|
private fun loadN(voiceNumber: Int, line: String, templateComments: MutableList<String>) {
|
||||||
val midiPitch = MidiPitch(voiceNumber)
|
|
||||||
midiPitch.currentVoiceNumber(voiceNumber)
|
|
||||||
midiPitch.initKey(meta["C"] ?: "C")
|
|
||||||
var midiDuration = 0
|
var midiDuration = 0
|
||||||
var nextDuration = 0
|
var nextDuration = 0
|
||||||
var lastNoteString = ""
|
var lastNoteString = ""
|
||||||
|
val midiPitch = MidiPitch(voiceNumber)
|
||||||
|
var nextComment: MutableList<String> = 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 = ' '
|
var lastIt = ' '
|
||||||
val newN = POneVoiceNote()
|
val newN = POneVoiceNote()
|
||||||
val lineRepeated = REGEX_REPETITION.replace(line) { matchResult ->
|
val lineRepeated = REGEX_REPETITION.replace(line) { matchResult ->
|
||||||
|
|
@ -377,17 +416,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
||||||
charIterated.repeat(nTimes)
|
charIterated.repeat(nTimes)
|
||||||
}
|
}
|
||||||
var commentNumber = -1
|
var commentNumber = -1
|
||||||
var nextComment: MutableList<String> = 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()
|
val lineChar = lineRepeated.toCharArray()
|
||||||
lineChar.forEach {
|
lineChar.forEach {
|
||||||
when (it) {
|
when (it) {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ class Transpose {
|
||||||
val octaveSigns = listOf("₄", "₃", "₂", "₁", "", "¹", "²", "³", "⁴")
|
val octaveSigns = listOf("₄", "₃", "₂", "₁", "", "¹", "²", "³", "⁴")
|
||||||
val noteToNumber = listOf("d", "di", "r", "ri", "m", "f", "fi", "s", "si", "l", "ta", "t")
|
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 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 {
|
fun compareNotes(note1: String, note2: String): Int {
|
||||||
val found1 = REGEX_ONE_NOTE.find(note1)
|
val found1 = REGEX_ONE_NOTE.find(note1)
|
||||||
val found2 = REGEX_ONE_NOTE.find(note2)
|
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<String, Int> {
|
fun transposeMidi(note: String, fromKey: String) : Pair<String, Int> {
|
||||||
return Pair(note, 4)
|
return Pair(note, 4)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue