Overload synth() and wave() functions
This commit is contained in:
@@ -3,7 +3,7 @@ package io.smnp.util.config
|
|||||||
import io.smnp.error.ShouldNeverReachThisLineException
|
import io.smnp.error.ShouldNeverReachThisLineException
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class MapConfig(private val map: Map<Value, Value>) {
|
class ConfigMap(private val map: Map<Value, Value>) {
|
||||||
private val raw by lazy { map.map { (key, value) -> key.unwrap() to value }.toMap() as Map<String, Value> }
|
private val raw by lazy { map.map { (key, value) -> key.unwrap() to value }.toMap() as Map<String, Value> }
|
||||||
|
|
||||||
operator fun get(key: String): Value {
|
operator fun get(key: String): Value {
|
||||||
@@ -19,6 +19,6 @@ class MapConfig(private val map: Map<Value, Value>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = MapConfig(emptyMap())
|
val EMPTY = ConfigMap(emptyMap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,25 +5,25 @@ import io.smnp.type.enumeration.DataType
|
|||||||
import io.smnp.type.matcher.Matcher
|
import io.smnp.type.matcher.Matcher
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class MapConfigSchema {
|
class ConfigMapSchema {
|
||||||
private data class Parameter(val matcher: Matcher, val required: Boolean, val default: Value)
|
private data class Parameter(val matcher: Matcher, val required: Boolean, val default: Value)
|
||||||
|
|
||||||
private val parameters = mutableMapOf<String, Parameter>()
|
private val parameters = mutableMapOf<String, Parameter>()
|
||||||
|
|
||||||
fun required(name: String, matcher: Matcher): MapConfigSchema {
|
fun required(name: String, matcher: Matcher): ConfigMapSchema {
|
||||||
parameters[name] = Parameter(matcher, true, Value.void())
|
parameters[name] = Parameter(matcher, true, Value.void())
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun optional(name: String, matcher: Matcher, default: Value = Value.void()): MapConfigSchema {
|
fun optional(name: String, matcher: Matcher, default: Value = Value.void()): ConfigMapSchema {
|
||||||
parameters[name] = Parameter(matcher, false, default)
|
parameters[name] = Parameter(matcher, false, default)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parse(config: Value): MapConfig {
|
fun parse(config: Value): ConfigMap {
|
||||||
val configMap = config.value as Map<Value, Value>
|
val configMap = config.value as Map<Value, Value>
|
||||||
|
|
||||||
return MapConfig(parameters.mapNotNull { (name, parameter) ->
|
return ConfigMap(parameters.mapNotNull { (name, parameter) ->
|
||||||
val value = configMap[Value.string(name)]
|
val value = configMap[Value.string(name)]
|
||||||
?: if (parameter.required) throw CustomException("The '$name' parameter of ${parameter.matcher} is required")
|
?: if (parameter.required) throw CustomException("The '$name' parameter of ${parameter.matcher} is required")
|
||||||
else parameter.default
|
else parameter.default
|
||||||
@@ -13,12 +13,12 @@ import io.smnp.type.matcher.Matcher.Companion.listOfMatchers
|
|||||||
import io.smnp.type.matcher.Matcher.Companion.mapOfMatchers
|
import io.smnp.type.matcher.Matcher.Companion.mapOfMatchers
|
||||||
import io.smnp.type.matcher.Matcher.Companion.ofType
|
import io.smnp.type.matcher.Matcher.Companion.ofType
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.util.config.MapConfig
|
import io.smnp.util.config.ConfigMap
|
||||||
import io.smnp.util.config.MapConfigSchema
|
import io.smnp.util.config.ConfigMapSchema
|
||||||
|
|
||||||
|
|
||||||
class MidiFunction : Function("midi") {
|
class MidiFunction : Function("midi") {
|
||||||
private val schema = MapConfigSchema()
|
private val schema = ConfigMapSchema()
|
||||||
.optional("bpm", ofType(INT), Value.int(120))
|
.optional("bpm", ofType(INT), Value.int(120))
|
||||||
.optional("ppq", ofType(INT))
|
.optional("ppq", ofType(INT))
|
||||||
.optional("output", ofType(STRING))
|
.optional("output", ofType(STRING))
|
||||||
@@ -47,7 +47,7 @@ class MidiFunction : Function("midi") {
|
|||||||
throw CustomException("MIDI standard supports max to 16 channels and that number has been exceeded")
|
throw CustomException("MIDI standard supports max to 16 channels and that number has been exceeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
Midi.with(MapConfig.EMPTY).play(unwrappedLines)
|
Midi.with(ConfigMap.EMPTY).play(unwrappedLines)
|
||||||
|
|
||||||
Value.void()
|
Value.void()
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ class MidiFunction : Function("midi") {
|
|||||||
throw CustomException("MIDI standard supports max to 16 channels and that number has been exceeded")
|
throw CustomException("MIDI standard supports max to 16 channels and that number has been exceeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
Midi.with(MapConfig.EMPTY).play(unwrappedChannels)
|
Midi.with(ConfigMap.EMPTY).play(unwrappedChannels)
|
||||||
|
|
||||||
Value.void()
|
Value.void()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import io.smnp.type.enumeration.DataType.*
|
|||||||
import io.smnp.type.matcher.Matcher.Companion.ofType
|
import io.smnp.type.matcher.Matcher.Companion.ofType
|
||||||
import io.smnp.type.matcher.Matcher.Companion.optional
|
import io.smnp.type.matcher.Matcher.Companion.optional
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.util.config.MapConfig
|
import io.smnp.util.config.ConfigMap
|
||||||
|
|
||||||
class MidiHelpFunction : Function("midiHelp") {
|
class MidiHelpFunction : Function("midiHelp") {
|
||||||
override fun define(new: FunctionDefinitionTool) {
|
override fun define(new: FunctionDefinitionTool) {
|
||||||
@@ -59,7 +59,7 @@ class MidiHelpFunction : Function("midiHelp") {
|
|||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
println(it)
|
println(it)
|
||||||
Midi
|
Midi
|
||||||
.with(MapConfig(mapOf(Value.string("bpm") to Value.int(bpm))))
|
.with(ConfigMap(mapOf(Value.string("bpm") to Value.int(bpm))))
|
||||||
.play(mapOf(channel to listOf(listOf("i:$instrument", it))))
|
.play(mapOf(channel to listOf(listOf("i:$instrument", it))))
|
||||||
Thread.sleep(100)
|
Thread.sleep(100)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package io.smnp.ext.midi
|
package io.smnp.ext.midi
|
||||||
|
|
||||||
import io.smnp.util.config.MapConfig
|
import io.smnp.util.config.ConfigMap
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.sound.midi.MidiSystem
|
import javax.sound.midi.MidiSystem
|
||||||
import javax.sound.midi.Sequence
|
import javax.sound.midi.Sequence
|
||||||
@@ -28,11 +28,11 @@ object Midi {
|
|||||||
sequencer.stop()
|
sequencer.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun with(config: MapConfig): SequenceExecutor {
|
fun with(config: ConfigMap): SequenceExecutor {
|
||||||
return SequenceExecutor(sequencer, config)
|
return SequenceExecutor(sequencer, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
class SequenceExecutor(private val sequencer: Sequencer, private val config: MapConfig) {
|
class SequenceExecutor(private val sequencer: Sequencer, private val config: ConfigMap) {
|
||||||
fun play(lines: List<List<Any>>) {
|
fun play(lines: List<List<Any>>) {
|
||||||
val sequence = Sequence(Sequence.PPQ, config.getUnwrappedOrDefault("ppq", DEFAULT_PPQ))
|
val sequence = Sequence(Sequence.PPQ, config.getUnwrappedOrDefault("ppq", DEFAULT_PPQ))
|
||||||
provideCompiler(config).compileLines(lines, sequence)
|
provideCompiler(config).compileLines(lines, sequence)
|
||||||
@@ -40,7 +40,7 @@ object Midi {
|
|||||||
writeToFile(sequence)
|
writeToFile(sequence)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun provideCompiler(config: MapConfig): SequenceCompiler =
|
private fun provideCompiler(config: ConfigMap): SequenceCompiler =
|
||||||
if (config.containsKey("ppq")) PpqSequenceCompiler()
|
if (config.containsKey("ppq")) PpqSequenceCompiler()
|
||||||
else DefaultSequenceCompiler()
|
else DefaultSequenceCompiler()
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,41 @@ package io.smnp.ext.function
|
|||||||
|
|
||||||
import io.smnp.callable.function.Function
|
import io.smnp.callable.function.Function
|
||||||
import io.smnp.callable.function.FunctionDefinitionTool
|
import io.smnp.callable.function.FunctionDefinitionTool
|
||||||
|
import io.smnp.callable.signature.Signature
|
||||||
import io.smnp.callable.signature.Signature.Companion.simple
|
import io.smnp.callable.signature.Signature.Companion.simple
|
||||||
import io.smnp.ext.synth.Synthesizer
|
import io.smnp.ext.synth.Synthesizer
|
||||||
import io.smnp.ext.synth.Wave
|
import io.smnp.ext.synth.Wave
|
||||||
|
import io.smnp.ext.synth.WaveCompiler
|
||||||
|
import io.smnp.type.enumeration.DataType
|
||||||
import io.smnp.type.enumeration.DataType.INT
|
import io.smnp.type.enumeration.DataType.INT
|
||||||
|
import io.smnp.type.matcher.Matcher
|
||||||
import io.smnp.type.matcher.Matcher.Companion.listOf
|
import io.smnp.type.matcher.Matcher.Companion.listOf
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
class SynthFunction : Function("synth") {
|
class SynthFunction : Function("synth") {
|
||||||
|
|
||||||
override fun define(new: FunctionDefinitionTool) {
|
override fun define(new: FunctionDefinitionTool) {
|
||||||
new function simple(listOf(INT)) body { _, (wave) ->
|
new function simple(listOf(INT)) body { _, (wave) ->
|
||||||
val bytes = (wave.value as List<Value>).map { (it.value as Int).toByte() }.toByteArray()
|
val bytes = (wave.value as List<Value>).map { (it.value as Int).toByte() }.toByteArray()
|
||||||
Synthesizer.synth(Wave(bytes))
|
Synthesizer.synth(Wave(bytes))
|
||||||
Value.void()
|
Value.void()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new function Signature.vararg(
|
||||||
|
listOf(DataType.NOTE, INT, DataType.STRING),
|
||||||
|
Matcher.mapOfMatchers(Matcher.ofType(DataType.STRING), Matcher.anyType())
|
||||||
|
) body { _, (config, vararg) ->
|
||||||
|
val compiler = WaveCompiler(config, Synthesizer.SAMPLING_RATE)
|
||||||
|
val wave = compiler.compileLines(vararg.unwrapCollections() as List<List<Value>>)
|
||||||
|
Synthesizer.synth(wave)
|
||||||
|
Value.void()
|
||||||
|
}
|
||||||
|
|
||||||
|
new function Signature.vararg(listOf(DataType.NOTE, INT, DataType.STRING)) body { _, (vararg) ->
|
||||||
|
val compiler = WaveCompiler(Value.map(emptyMap()), Synthesizer.SAMPLING_RATE)
|
||||||
|
val wave = compiler.compileLines(vararg.unwrapCollections() as List<List<Value>>)
|
||||||
|
Synthesizer.synth(wave)
|
||||||
|
Value.void()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,6 @@ import io.smnp.type.model.Value
|
|||||||
|
|
||||||
class WaveFunction : Function("wave") {
|
class WaveFunction : Function("wave") {
|
||||||
|
|
||||||
|
|
||||||
override fun define(new: FunctionDefinitionTool) {
|
override fun define(new: FunctionDefinitionTool) {
|
||||||
new function Signature.vararg(
|
new function Signature.vararg(
|
||||||
listOf(NOTE, INT, STRING),
|
listOf(NOTE, INT, STRING),
|
||||||
@@ -26,5 +25,13 @@ class WaveFunction : Function("wave") {
|
|||||||
|
|
||||||
Value.list(wave.bytes.map { Value.int(it.toInt()) }.toList())
|
Value.list(wave.bytes.map { Value.int(it.toInt()) }.toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new function Signature.vararg(listOf(NOTE, INT, STRING)) body { _, (vararg) ->
|
||||||
|
val compiler = WaveCompiler(Value.map(emptyMap()), Synthesizer.SAMPLING_RATE)
|
||||||
|
|
||||||
|
val wave = compiler.compileLines(vararg.unwrapCollections() as List<List<Value>>)
|
||||||
|
|
||||||
|
Value.list(wave.bytes.map { Value.int(it.toInt()) }.toList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,10 @@ package io.smnp.ext.synth
|
|||||||
import io.smnp.type.enumeration.DataType.FLOAT
|
import io.smnp.type.enumeration.DataType.FLOAT
|
||||||
import io.smnp.type.matcher.Matcher.Companion.ofType
|
import io.smnp.type.matcher.Matcher.Companion.ofType
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.util.config.MapConfigSchema
|
import io.smnp.util.config.ConfigMapSchema
|
||||||
|
|
||||||
object AdsrEnvelopeFactory : EnvelopeFactory {
|
object AdsrEnvelopeFactory : EnvelopeFactory {
|
||||||
private val schema = MapConfigSchema()
|
private val schema = ConfigMapSchema()
|
||||||
.required("p1", ofType(FLOAT))
|
.required("p1", ofType(FLOAT))
|
||||||
.required("p2", ofType(FLOAT))
|
.required("p2", ofType(FLOAT))
|
||||||
.required("p3", ofType(FLOAT))
|
.required("p3", ofType(FLOAT))
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import io.smnp.error.CustomException
|
|||||||
import io.smnp.type.enumeration.DataType
|
import io.smnp.type.enumeration.DataType
|
||||||
import io.smnp.type.matcher.Matcher
|
import io.smnp.type.matcher.Matcher
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.util.config.MapConfigSchema
|
import io.smnp.util.config.ConfigMapSchema
|
||||||
|
|
||||||
interface EnvelopeFactory {
|
interface EnvelopeFactory {
|
||||||
fun createEnvelope(config: Value): Envelope
|
fun createEnvelope(config: Value): Envelope
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val schema = MapConfigSchema()
|
private val schema = ConfigMapSchema()
|
||||||
.required("name", Matcher.ofType(DataType.STRING))
|
.required("name", Matcher.ofType(DataType.STRING))
|
||||||
|
|
||||||
private val factories = mapOf(
|
private val factories = mapOf(
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import io.smnp.math.Fraction
|
|||||||
import io.smnp.type.enumeration.DataType
|
import io.smnp.type.enumeration.DataType
|
||||||
import io.smnp.type.matcher.Matcher
|
import io.smnp.type.matcher.Matcher
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.util.config.MapConfigSchema
|
import io.smnp.util.config.ConfigMapSchema
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
class WaveCompiler(config: Value, private val samplingRate: Double) {
|
class WaveCompiler(config: Value, private val samplingRate: Double) {
|
||||||
private val semitone = 2.0.pow(1.0 / 12.0)
|
private val semitone = 2.0.pow(1.0 / 12.0)
|
||||||
private val schema = MapConfigSchema()
|
private val schema = ConfigMapSchema()
|
||||||
.optional("bpm", Matcher.ofType(DataType.INT), Value.int(120))
|
.optional("bpm", Matcher.ofType(DataType.INT), Value.int(120))
|
||||||
.optional(
|
.optional(
|
||||||
"overtones", Matcher.listOf(DataType.FLOAT), Value.list(
|
"overtones", Matcher.listOf(DataType.FLOAT), Value.list(
|
||||||
|
|||||||
Reference in New Issue
Block a user