Add global commands to config map of smnp.audio.midi module
This commit is contained in:
@@ -22,6 +22,8 @@ class MidiFunction : Function("midi") {
|
|||||||
.optional("ppq", ofType(INT))
|
.optional("ppq", ofType(INT))
|
||||||
.optional("output", ofType(STRING))
|
.optional("output", ofType(STRING))
|
||||||
.optional("play", ofType(BOOL))
|
.optional("play", ofType(BOOL))
|
||||||
|
.optional("velocity", ofType(FLOAT), Value.float(1.0F))
|
||||||
|
.optional("instrument", ofType(INT), Value.int(0))
|
||||||
|
|
||||||
override fun define(new: FunctionDefinitionTool) {
|
override fun define(new: FunctionDefinitionTool) {
|
||||||
new function vararg(
|
new function vararg(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.smnp.ext.midi.lib.compiler
|
|||||||
import io.smnp.data.entity.Note
|
import io.smnp.data.entity.Note
|
||||||
import io.smnp.error.CustomException
|
import io.smnp.error.CustomException
|
||||||
import io.smnp.error.ShouldNeverReachThisLineException
|
import io.smnp.error.ShouldNeverReachThisLineException
|
||||||
|
import io.smnp.util.config.ConfigMap
|
||||||
import javax.sound.midi.MidiEvent
|
import javax.sound.midi.MidiEvent
|
||||||
import javax.sound.midi.Sequence
|
import javax.sound.midi.Sequence
|
||||||
import javax.sound.midi.ShortMessage
|
import javax.sound.midi.ShortMessage
|
||||||
@@ -24,11 +25,14 @@ abstract class SequenceCompiler {
|
|||||||
const val ZERO = 0x00
|
const val ZERO = 0x00
|
||||||
}
|
}
|
||||||
|
|
||||||
fun compileLines(lines: List<List<Any>>, sequence: Sequence) =
|
fun compileChannels(channels: Map<Int, List<List<Any>>>, config: ConfigMap, sequence: Sequence) {
|
||||||
lines.forEachIndexed { channel, line -> compileLine(line, channel, sequence) }
|
channels.forEach { (channel, lines) ->
|
||||||
|
lines.forEach { line -> compileLine(line, channel - 1, config, sequence) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun compileChannels(channels: Map<Int, List<List<Any>>>, sequence: Sequence) = channels.forEach { (channel, lines) ->
|
fun compileLines(lines: List<List<Any>>, config: ConfigMap, sequence: Sequence) {
|
||||||
lines.forEach { line -> compileLine(line, channel - 1, sequence) }
|
lines.forEachIndexed { channel, line -> compileLine(line, channel, config, sequence) }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun compileNote(
|
protected abstract fun compileNote(
|
||||||
@@ -42,10 +46,12 @@ abstract class SequenceCompiler {
|
|||||||
|
|
||||||
abstract fun compileRest(noteOnTick: Long, item: Int, ppq: Int): Long
|
abstract fun compileRest(noteOnTick: Long, item: Int, ppq: Int): Long
|
||||||
|
|
||||||
private fun compileLine(line: List<Any>, channel: Int, sequence: Sequence) {
|
private fun compileLine(line: List<Any>, channel: Int, config: ConfigMap, sequence: Sequence) {
|
||||||
val track = sequence.createTrack()
|
val track = sequence.createTrack()
|
||||||
|
|
||||||
var velocity = 127
|
track.add(event(Command.PROGRAM_CHANGE + channel, channel, config["instrument"].value as Int, 0, 0))
|
||||||
|
var velocity = velocityToInt(config["velocity"].value as Float)
|
||||||
|
|
||||||
val lastTick = line.fold(0L) { noteOnTick, item ->
|
val lastTick = line.fold(0L) { noteOnTick, item ->
|
||||||
when (item) {
|
when (item) {
|
||||||
is Note -> compileNote(item, velocity, channel, noteOnTick, track, sequence.resolution)
|
is Note -> compileNote(item, velocity, channel, noteOnTick, track, sequence.resolution)
|
||||||
@@ -59,7 +65,7 @@ abstract class SequenceCompiler {
|
|||||||
|
|
||||||
command["velocity"]?.let {
|
command["velocity"]?.let {
|
||||||
val value = it as? Float ?: throw CustomException("Invalid parameter type: 'velocity' is supposed to be of float type")
|
val value = it as? Float ?: throw CustomException("Invalid parameter type: 'velocity' is supposed to be of float type")
|
||||||
velocity = (127.0 * value.coerceIn(0.0F, 1.0F)).toInt()
|
velocity = velocityToInt(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
noteOnTick
|
noteOnTick
|
||||||
@@ -71,6 +77,8 @@ abstract class SequenceCompiler {
|
|||||||
track.add(allNotesOff(channel, lastTick))
|
track.add(allNotesOff(channel, lastTick))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun velocityToInt(value: Float) = (127.0 * value.coerceIn(0.0F, 1.0F)).toInt()
|
||||||
|
|
||||||
protected fun noteOn(note: Note, velocity: Int, channel: Int, tick: Long): MidiEvent {
|
protected fun noteOn(note: Note, velocity: Int, channel: Int, tick: Long): MidiEvent {
|
||||||
return event(Command.NOTE_ON, channel, note.intPitch() + 12, velocity, tick)
|
return event(Command.NOTE_ON, channel, note.intPitch() + 12, velocity, tick)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ object Midi {
|
|||||||
val sequence = Sequence(Sequence.PPQ, config.getUnwrappedOrDefault("ppq",
|
val sequence = Sequence(Sequence.PPQ, config.getUnwrappedOrDefault("ppq",
|
||||||
DEFAULT_PPQ
|
DEFAULT_PPQ
|
||||||
))
|
))
|
||||||
provideCompiler(config).compileLines(lines, sequence)
|
provideCompiler(config).compileLines(lines, config, sequence)
|
||||||
play(sequence)
|
play(sequence)
|
||||||
writeToFile(sequence)
|
writeToFile(sequence)
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ object Midi {
|
|||||||
val sequence = Sequence(Sequence.PPQ, config.getUnwrappedOrDefault("ppq",
|
val sequence = Sequence(Sequence.PPQ, config.getUnwrappedOrDefault("ppq",
|
||||||
DEFAULT_PPQ
|
DEFAULT_PPQ
|
||||||
))
|
))
|
||||||
provideCompiler(config).compileChannels(channels, sequence)
|
provideCompiler(config).compileChannels(channels, config, sequence)
|
||||||
play(sequence)
|
play(sequence)
|
||||||
writeToFile(sequence)
|
writeToFile(sequence)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user