Add basic support for configMap-based commands in smnp.audio.midi module

This commit is contained in:
2020-04-06 13:40:38 +02:00
parent af48d6b640
commit 7fbb81c38b
3 changed files with 28 additions and 37 deletions

View File

@@ -8,6 +8,7 @@ class DefaultSequenceCompiler : SequenceCompiler() {
override fun compileNote(
item: Note,
velocity: Int,
channel: Int,
noteOnTick: Long,
track: Track,
@@ -15,8 +16,8 @@ class DefaultSequenceCompiler : SequenceCompiler() {
): Long {
val noteDuration = (4L * ppq * item.duration.decimal).toLong()
val noteOffTick = noteOnTick + noteDuration
track.add(noteOn(item, channel, noteOnTick))
track.add(noteOff(item, channel, noteOffTick))
track.add(noteOn(item, velocity, channel, noteOnTick))
track.add(noteOff(item, velocity, channel, noteOffTick))
return noteOffTick
}
}

View File

@@ -8,14 +8,15 @@ class PpqSequenceCompiler : SequenceCompiler() {
override fun compileNote(
item: Note,
velocity: Int,
channel: Int,
noteOnTick: Long,
track: Track,
ppq: Int
): Long {
val noteOffTick = noteOnTick + item.duration.denominator
track.add(noteOn(item, channel, noteOnTick))
track.add(noteOff(item, channel, noteOffTick))
track.add(noteOn(item, velocity, channel, noteOnTick))
track.add(noteOff(item, velocity, channel, noteOffTick))
return noteOffTick
}
}

View File

@@ -1,7 +1,7 @@
package io.smnp.ext.midi.lib.compiler
import io.smnp.data.entity.Note
import io.smnp.error.EvaluationException
import io.smnp.error.CustomException
import io.smnp.error.ShouldNeverReachThisLineException
import javax.sound.midi.MidiEvent
import javax.sound.midi.Sequence
@@ -33,6 +33,7 @@ abstract class SequenceCompiler {
protected abstract fun compileNote(
item: Note,
velocity: Int,
channel: Int,
noteOnTick: Long,
track: Track,
@@ -44,11 +45,25 @@ abstract class SequenceCompiler {
private fun compileLine(line: List<Any>, channel: Int, sequence: Sequence) {
val track = sequence.createTrack()
var velocity = 127
val lastTick = line.fold(0L) { noteOnTick, item ->
when (item) {
is Note -> compileNote(item, channel, noteOnTick, track, sequence.resolution)
is Note -> compileNote(item, velocity, channel, noteOnTick, track, sequence.resolution)
is Int -> compileRest(noteOnTick, item, sequence.resolution)
is String -> command(item, channel, noteOnTick, track)
is Map<*, *> -> {
val command = item as Map<String, Any>
command["instrument"]?.let {
val value = it as? Int ?: throw CustomException("Invalid parameter type: 'instrument' is supposed to be of int type")
track.add(event(Command.PROGRAM_CHANGE + channel, channel, value, 0, noteOnTick))
}
command["velocity"]?.let {
val value = it as? Float ?: throw CustomException("Invalid parameter type: 'velocity' is supposed to be of float type")
velocity = (127.0 * value).toInt()
}
noteOnTick
}
else -> throw ShouldNeverReachThisLineException()
}
}
@@ -56,38 +71,12 @@ abstract class SequenceCompiler {
track.add(allNotesOff(channel, lastTick))
}
private fun command(instruction: String, channel: Int, beginTick: Long, track: Track): Long {
if (instruction.isBlank()) {
throw EvaluationException("Empty strings are not allowed here")
}
val commandWithArguments = instruction.split(":")
val (command, args) = if (commandWithArguments.size == 2) commandWithArguments else listOf(
commandWithArguments[0],
"0,0"
)
val arguments = args.split(",")
val cmdCode = when (command) {
"i" -> Command.PROGRAM_CHANGE + channel
else -> throw EvaluationException("Unknown command '$command'")
}
track.add(
event(
cmdCode,
channel,
arguments.getOrNull(0)?.toInt() ?: 0,
arguments.getOrNull(1)?.toInt() ?: 0,
beginTick
)
)
return beginTick
protected fun noteOn(note: Note, velocity: Int, channel: Int, tick: Long): MidiEvent {
return event(Command.NOTE_ON, channel, note.intPitch() + 12, velocity, tick)
}
protected fun noteOn(note: Note, channel: Int, tick: Long): MidiEvent {
return event(Command.NOTE_ON, channel, note.intPitch() + 12, 127, tick)
}
protected fun noteOff(note: Note, channel: Int, tick: Long): MidiEvent {
return event(Command.NOTE_OFF, channel, note.intPitch() + 12, 127, tick)
protected fun noteOff(note: Note, velocity: Int, channel: Int, tick: Long): MidiEvent {
return event(Command.NOTE_OFF, channel, note.intPitch() + 12, velocity, tick)
}
private fun allNotesOff(channel: Int, tick: Long): MidiEvent {