Overload synth() and wave() functions

This commit is contained in:
2020-03-20 19:41:03 +01:00
parent 900d3849f1
commit ff45e7e479
10 changed files with 54 additions and 25 deletions

View File

@@ -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())
} }
} }

View File

@@ -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

View File

@@ -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()
} }

View File

@@ -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)
} }

View File

@@ -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()

View File

@@ -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()
}
} }
} }

View File

@@ -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())
}
} }
} }

View File

@@ -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))

View File

@@ -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(

View File

@@ -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(