diff options
Diffstat (limited to 'BowelyzerConfig.sc')
-rw-r--r-- | BowelyzerConfig.sc | 402 |
1 files changed, 226 insertions, 176 deletions
diff --git a/BowelyzerConfig.sc b/BowelyzerConfig.sc index 4079119..e092b25 100644 --- a/BowelyzerConfig.sc +++ b/BowelyzerConfig.sc @@ -21,25 +21,60 @@ BowelyzerConfig{ const <controlsWithRanger = #[ \test]; const <controlsWithKnob = #[ \test]; + const <hasToBeString = #[ + \forwardAddress, + \hubAddress, + \synthServerAddress + ]; + + const <hasToBeInteger = #[ + \forwardPort, + \hubPort, + \synthServerPort, + \pitchMedian, + \pitchDownSample + ]; + const <hasToBeDictionary = #[ + \inputs, + \controls, + \left, + \right + ]; + const <hasToBeFloat = #[ + \amplitudeAttackTime, + \amplitudeReleaseTime, + \hfHainsworth, + \hfFoote, + \hfThreshold, + \hfWaitTime, + \pitchInitFreq, + \pitchMinFreq, + \pitchMaxFreq, + \pitchExecFreq, + \pitchMaxBinsPerOctave, + \pitchAmpThreshold, + \pitchPeakThreshold, + \sendReplyFreq + ]; + var <config, <defaultConfig, <defaultControls, - <changed = false, - <hasToBeString, - <hasToBeInteger, - <hasToBeArray, - <hasToBeDictionary, - <hasToBeFloat; + <changed = false; +// <hasToBeString, +// <hasToBeInteger, +// <hasToBeDictionary, +// <hasToBeFloat; *controlContainedIn{ arg control; - if(BowelyzerConfig.controlsWithSlider.asString.contains(control.asString),{ + if(BowelyzerConfig.controlsWithSlider.contains(control),{ ^\slider; }); - if(BowelyzerConfig.controlsWithRanger.asString.contains(control.asString),{ + if(BowelyzerConfig.controlsWithRanger.contains(control),{ ^\ranger; }); - if(BowelyzerConfig.controlsWithKnob.asString.contains(control.asString),{ + if(BowelyzerConfig.controlsWithKnob.contains(control),{ ^\knob; }); } @@ -51,91 +86,77 @@ BowelyzerConfig{ init{ arg configFile; - //TODO: Use ControlSpec to setup standard values/boundaries - hasToBeString = [ - \forwardAddress, - \hubAddress, - \synthServerAddress - ]; - hasToBeInteger = [ - \forwardPort, - \hubPort, - \synthServerPort, - \pitchMedian, - \pitchDownSample - ]; - hasToBeArray = [ - \names - ]; - hasToBeDictionary = [ - \inputs, - \controls, - \left, - \right - ]; - hasToBeFloat = [ - \amplitudeAttackTime, - \amplitudeReleaseTime, - \hfHainsworth, - \hfFoote, - \hfThreshold, - \hfWaitTime, - \pitchInitFreq, - \pitchMinFreq, - \pitchMaxFreq, - \pitchExecFreq, - \pitchMaxBinsPerOctave, - \pitchAmpThreshold, - \pitchPeakThreshold, - \sendReplyFreq - ]; - config = this.createDefaultConfig; + this.addControlSpecs; + this.createDefaultConfig; + config = Dictionary.new; if(configFile.notNil,{ - this.readConfigurationFile(configFile) + if(this.readConfigurationFile(configFile).not,{ + error("Reading of configuration file failed. Using default."); + config = defaultConfig; + }); },{ ("No configuration file provided. Using default.").postln; + config = defaultConfig; }); this.showConfig; } + addControlSpecs{ + ControlSpec.specs[\amplitudeAttackTime] = ControlSpec(0.001, 10, \lin, 0.001, 0.01, units: "s"); + ControlSpec.specs[\amplitudeReleaseTime] = ControlSpec(0.001, 10, \lin, 0.001, 0.01, units: "s"); + ControlSpec.specs[\hfHainsworth] = ControlSpec(0.0, 1.0, \lin, 0.001, 0.0, units: ""); + ControlSpec.specs[\hfFoote] = ControlSpec(0.0, 1.0, \lin, 0.001, 0.0, units: ""); + ControlSpec.specs[\hfThreshold] = ControlSpec(0.0, 1.0, \lin, 0.001, 1.0, units: ""); + ControlSpec.specs[\hfWaitTime] = ControlSpec(0.0, 1.0, \lin, 0.001, 0.04, units: "s"); + ControlSpec.specs[\pitchInitFreq] = ControlSpec(20.0, 20000.0, \exp, 0.01, 440.0, units: "Hz"); + ControlSpec.specs[\pitchMinFreq] = ControlSpec(20.0, 20000.0, \exp, 0.01, 60.0, units: "Hz"); + ControlSpec.specs[\pitchMaxFreq] = ControlSpec(20.0, 20000.0, \exp, 0.01, 4000.0, units: "Hz"); + ControlSpec.specs[\pitchExecFreq] = ControlSpec(20.0, 20000.0, \exp, 0.01, 100.0, units: "Hz"); + ControlSpec.specs[\pitchMaxBinsPerOctave] = ControlSpec(1, 64, \lin, 1, 16, units: "bins"); + ControlSpec.specs[\pitchMedian] = ControlSpec(1, 64, \lin, 1, 1, units: ""); + ControlSpec.specs[\pitchAmpThreshold] = ControlSpec(0.01, 1.0, \lin, 0.01, 0.01, units: "dB"); + ControlSpec.specs[\pitchPeakThreshold] = ControlSpec(0.01, 1.0, \lin, 0.01, 0.5, units: "dB"); + ControlSpec.specs[\pitchDownSample] = ControlSpec(0.01, 1.0, \lin, 0.01, 0.5, units: "dB"); + ControlSpec.specs[\sendReplyFreq] = ControlSpec(1, 100, \exp, 0.1, 20, units: "Hz"); + } + // create the default configuration createDefaultConfig{ defaultControls = Dictionary.with(*[ - "amplitudeAttackTime" -> 0.01, - "amplitudeReleaseTime" -> 0.1, - "hfHainsworth" -> 1.0, - "hfFoote" -> 0.0, - "hfThreshold" -> 0.3, - "hfWaitTime" -> 0.04, - "pitchInitFreq" -> 440, - "pitchMinFreq" -> 60, - "pitchMaxFreq" -> 4000, - "pitchExecFreq" -> 100, - "pitchMaxBinsPerOctave" -> 16, - "pitchMedian" -> 1, - "pitchAmpThreshold" -> 0.01, - "pitchPeakThreshold" -> 0.5, - "pitchDownSample" -> 1, - "sendReplyFreq" ->20 + \amplitudeAttackTime -> 0.01, + \amplitudeReleaseTime -> 0.1, + \hfHainsworth -> 1.0, + \hfFoote -> 0.0, + \hfThreshold -> 0.3, + \hfWaitTime -> 0.04, + \pitchInitFreq -> 440, + \pitchMinFreq -> 60, + \pitchMaxFreq -> 4000, + \pitchExecFreq -> 100, + \pitchMaxBinsPerOctave -> 16, + \pitchMedian -> 1, + \pitchAmpThreshold -> 0.01, + \pitchPeakThreshold -> 0.5, + \pitchDownSample -> 1, + \sendReplyFreq ->20 ]); defaultConfig = Dictionary.with(*[ - "inputs" -> Dictionary.with(*[ - "left" -> 0, - "right" -> 1 + \inputs -> Dictionary.with(*[ + \left -> 0, + \right -> 1 ]), - "names" -> ["left", "right"], - "synthServerAddress" -> "127.0.0.1", - "synthServerPort"->57110, - "hubAddress" -> "127.0.0.1", - "hubPort" -> 57120, - "forwardAddress" -> "127.0.0.1", - "forwardPort" -> 9999, - "controls" -> Dictionary.with(*[ - "left" -> defaultControls, - "right" -> defaultControls + //names -> [left, right], + \synthServerAddress -> "127.0.0.1", + \synthServerPort -> 57110, + \hubAddress -> "127.0.0.1", + \hubPort -> 57120, + \forwardAddress -> "127.0.0.1", + \forwardPort -> 57120, + \controls -> Dictionary.with(*[ + \left -> defaultControls, + \right -> defaultControls ]) ]); - ^defaultConfig; } // read configuration from file @@ -148,133 +169,162 @@ BowelyzerConfig{ try{ configFromFile = (configFile.asString).parseYAMLFile; configFromFile.keysValuesDo({|key, value| - if(config.includesKey(key),{ - config.put(key,value); - }); + switch(key.asSymbol, + \controls,{ + // adding names dictionary + config.put(key.asSymbol, Dictionary.new(value.size)); + // going through the names and their associated dictionaries + value.keysValuesDo({|name, dictionary| + if(dictionary.isKindOf(Dictionary),{ + config.at(key.asSymbol).put(name.asSymbol, Dictionary.new(dictionary.size)); + // going through each control dictionary with controls and their values + dictionary.keysValuesDo({|control, controlValue| + if(defaultControls.includesKey(control.asSymbol),{ + config.at(key.asSymbol).at(name.asSymbol).put(control.asSymbol, this.getControlValue(control.asSymbol, controlValue)); + }); + }); + },{ + error("This element should be of type Dictionary: "++dictionary); + }); + }); + }, + \inputs,{ + config.put(key.asSymbol, Dictionary.new()); + value.keysValuesDo({|name, channel| + config.at(\inputs).put(name.asSymbol, channel.asInteger); + }); + }, + { + if(defaultConfig.includesKey(key.asSymbol),{ + config.put(key.asSymbol, this.getSettingValue(key.asSymbol, value)); + },{ + error("Key is not included in known configuration: "++key); + }); + } + ); + }); + postln("Reading of configuration complete. Now validating..."); + if(this.validateConfig,{ + postln("Validation complete."); + ^true; + },{ + postln("Validation failed."); + ^false; }); - this.validateConfig; }{ ("Failed parsing the file: "++configFile).error; ("Make sure its JSON syntax is valid!").error; + ^false; }; }, { error("File not readable:"++configFile.asString); + ^false; }); } - //check common mistakes in configuration and try fixing them - validateConfig{ - var broken = false; - //check for correct types - config.keysValuesDo({|key, value| - if(key.isKindOf(String).not,{ - error("Key is no string: "++key); - broken = true; + // return a control value as correct type and within its defined ControlSpec range + getControlValue{ + arg control, value; + if(defaultControls.includesKey(control.asSymbol),{ + if(hasToBeFloat.includes(control.asSymbol),{ + ^control.asSymbol.asSpec.constrain(value.asFloat).asFloat; }); - // check if the strings are associated with the correct keys - if(hasToBeString.includes(key.asSymbol) && value.isKindOf(String).not, { - config.put(key, value.asString); + if(hasToBeInteger.includes(control.asSymbol),{ + ^control.asSymbol.asSpec.constrain(value.asInteger).asInteger; }); - // check if the integers are associated with the correct keys - if(hasToBeInteger.includes(key.asSymbol) && value.isKindOf(Integer).not, { - config.put(key, value.asInteger); - }); - // check if the floats are associated with the correct keys - if(hasToBeFloat.includes(key.asSymbol) && value.isKindOf(Float).not, { - config.put(key, value.asFloat); + },{ + ^nil; + }); + } + + //get a setting value as correct type + getSettingValue{ + arg setting, value; + if(defaultConfig.includesKey(setting.asSymbol),{ + if(hasToBeString.includes(setting.asSymbol),{ + ^value.asString; }); - // check if the dictionaries are associated with the correct keys - if(hasToBeDictionary.includes(key.asSymbol) && value.isKindOf(Dictionary).not, { - error("Value ("++value++") of key ("++key++") should be of type Dictionary."); - broken = true; + if(hasToBeInteger.includes(setting.asSymbol),{ + ^value.asInteger; }); - //check if arrays are associated with the correct keys - if(hasToBeArray.includes(key.asSymbol),{ - if(value.isArray.not,{ - error("Value ("++value++") of key ("++key++") should be Array."); - broken = true; - },{ - //setting correct types in Arrays - if((key == "names") && (value.size >= 1) && value[0].isKindOf(String).not,{ - for(0, value.size-1, {|i| - config.at(key)[i] = value[i].asString; - }); - }); - }); - }); - // check if controls dictionaries are setup correctly - if(key == "controls",{ - value.keysValuesDo({|name, controls| - if(hasToBeDictionary.includes(name.asSymbol) && controls.isKindOf(Dictionary).not, { - error("Value ("++controls++") of key ("++name++") should be of type Dictionary."); - broken = true; - },{ - config.at("controls").at(name).keysValuesDo({|controlKey, controlValue| - // check if the integers are associated with the correct keys - if(hasToBeInteger.includes(controlKey.asSymbol) && controlValue.isKindOf(Integer).not, { - config.at(key).at(name).put(controlKey, controlValue.asInteger); - }); - // check if the floats are associated with the correct keys - if(hasToBeFloat.includes(controlKey.asSymbol) && controlValue.isKindOf(Float).not, { - config.at(key).at(name).put(controlKey, controlValue.asFloat); - }); - }); - }); + },{ + ^nil; + }); + } + + // validate/extend/fix the config and return true if it's useable + validateConfig{ + var broken = false; + //fail if there are no inputs defined + if(config.includesKey(\inputs).not,{ + ^false; + }); + // if there are no controls defined at all, add at least a Dictionary for them + if(config.includesKey(\controls).not,{ + config.put(\controls, Dictionary.new(config.at(\inputs).size)); + }); + // go through the defaultConfig/defaultControl collection and compare it to the provided config + defaultConfig.keysValuesDo({|key, value| + if(key != \controls && key != \inputs,{ + // if a standard setting is not in the configuration, add it from the default + if(config.includesKey(key).not,{ + config.put(key, value); }); }); - //check if inputs dictionaries are set up correctly - if(key == "inputs", { - value.keysValuesDo({|name, input| - if(input.isKindOf(Integer).not, { - error("Value ("++input++") of key ("++name++") should be of type Integer."); - config.at("inputs").put(name -> input.asInteger); - }); - }); + }); + // go through the inputs and add controls, if there are none setup for them + config.at(\inputs).keysValuesDo({|name, channel| + // if there are no controls defined for the input, add them from default + if(config.at(\controls).includesKey(name).not,{ + config.at(\controls).put(name, defaultControls); }); }); - // if not completely broken, fix stuff - if(broken,{ - error("There were serious errors. Reverting to default config."); - config = this.createDefaultConfig; - },{ - postln("Fixing stuff"); - // disable and zero-pad channels without inputs - if(config.at("names").size > config.at("inputs").size,{ - error("More channels than inputs defined. Disabling missing."); - config.at("names").do({|name,i| - if(config.at("inputs").includesKey(name).not,{ - config.at("inputs").put(name.asString -> 0); - //TODO: Disable here. + // go through each control dictionary + config.at(\controls).keysValuesDo({|name, controlDictionary| + if(config.at(\inputs).includesKey(name),{ + // go through the defaultControl Dictionary and add missing controls to controls from config + defaultControls.keysValuesDo({|defaultControl, defaultControlValue| + if(controlDictionary.includesKey(defaultControl).not,{ + config.at(\controls).at(name).put(defaultControl, defaultControlValue); }); }); + },{ + // remove controls for non-existing inputs + config.at(\controls).removeAt(name); }); - //TODO: add defaultControls for channels that have none - //TODO: fill non-existing keys with default values (in case config file is incomplete) }); + ^true; } //print the current config showConfig{ - postln("Configuration is:"); + postln("Configuration is: "); config.keysValuesDo{|key, value| - if(key == "controls",{ - postln(key++" ("++key.class++") ->"); - value.keysValuesDo{|key, value| - postln( key++" ("++key.class++"):"); - value.keysValuesDo{|key, value| - postln(" "++key++" ("++key.class++") -> "++value++" ("++value.class++")"); - } - } - },{ - postln(key++" ("++key.class++") -> "++value++" ("++value.class++")"); - }); + switch(key, + \controls, { + postln(key++" ("++key.class++") -> ("++value.class++")"); + value.keysValuesDo({|subKey, subValue| + postln(" "++subKey++" ("++subKey.class++") -> ("++subValue.class++")"); + subValue.keysValuesDo({|subSubKey, subSubValue| + postln(" "++subSubKey++" ("++subSubKey.class++") -> "++subSubValue++" ("++subSubValue.class++")"); + }); + }); + }, + \inputs, { + postln(key++" ("++key.class++") -> ("++value.class++")"); + value.keysValuesDo({|subKey, subValue| + postln(" "++subKey++" ("++subKey.class++") -> "++subValue++" ("++subValue.class++")"); + }); + }, + {postln(key++" ("++key.class++") -> "++value++" ("++value.class++")");} + ); }; } getSynthControl{ arg name, control; - if(config.at("controls").includesKey(name) && config.at("controls").at(name).includes(control),{ - ^config.at("controls").at(name).at(control); + if(config.at(\controls).includesKey(name) && config.at(\controls).at(name).includes(control),{ + ^config.at(\controls).at(name).at(control); },{ ^false; }); @@ -283,8 +333,8 @@ BowelyzerConfig{ // sets a control to a value for a synth setSynthControl{ arg name, control; - if(config.at("controls").includesKey(name) && config.at("controls").at(name).includes(control[0]),{ - config.at("controls").at(name).put(control[0], control[1]); + if(config.at(\controls).includesKey(name) && config.at(\controls).at(name).includes(control[0]),{ + config.at(\controls).at(name).put(control[0], control[1]); ^true; },{ ^false; |