aboutsummaryrefslogtreecommitdiffstats
path: root/classes/BowelyzerConfig.sc
diff options
context:
space:
mode:
Diffstat (limited to 'classes/BowelyzerConfig.sc')
-rw-r--r--classes/BowelyzerConfig.sc362
1 files changed, 362 insertions, 0 deletions
diff --git a/classes/BowelyzerConfig.sc b/classes/BowelyzerConfig.sc
new file mode 100644
index 0000000..287aad4
--- /dev/null
+++ b/classes/BowelyzerConfig.sc
@@ -0,0 +1,362 @@
+BowelyzerConfig{
+
+ const <controlsWithSlider = #[
+ \amplitudeAttackTime,
+ \amplitudeReleaseTime,
+ \hfHainsworth,
+ \hfFoote,
+ \hfThreshold,
+ \hfWaitTime,
+ \pitchInitFreq,
+ \pitchMinFreq,
+ \pitchMaxFreq,
+ \pitchExecFreq,
+ \pitchMaxBinsPerOctave,
+ \pitchMedian,
+ \pitchAmpThreshold,
+ \pitchPeakThreshold,
+ \pitchDownSample,
+ \sendReplyFreq
+ ];
+ 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
+ ];
+
+ const <hasToBeBool = #[
+ \active
+ ];
+
+ var <config,
+ <defaultConfig,
+ <defaultControls,
+ <changed = false;
+
+ *controlContainedIn{
+ arg control;
+ if(BowelyzerConfig.controlsWithSlider.includes(control),{
+ ^\slider;
+ });
+ if(BowelyzerConfig.controlsWithRanger.includes(control),{
+ ^\ranger;
+ });
+ if(BowelyzerConfig.controlsWithKnob.includes(control),{
+ ^\knob;
+ });
+ }
+
+ *new{
+ arg config;
+ ^super.new.init(config);
+ }
+
+ init{
+ arg configFile;
+ this.addControlSpecs;
+ this.createDefaultConfig;
+ config = Dictionary.new;
+ if(configFile.notNil,{
+ 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;
+ }
+
+ // add ControlSpecs to the global Dictionary
+ 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, 1.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(1, 100, \lin, 1, 1, units: "samples");
+ ControlSpec.specs[\sendReplyFreq] = ControlSpec(1, 100, \exp, 0.1, 20, units: "Hz");
+ ControlSpec.specs[\input] = ControlSpec(0, Server.default.options.numInputBusChannels-1, \lin, 1, 0, units: "channels");
+ ControlSpec.specs[\port] = ControlSpec(1, 65535, \lin, 1, 0, units: "ports");
+ }
+
+ // create the default configuration
+ createDefaultConfig{
+ defaultControls = Dictionary.with(*[
+ \active -> true,
+ \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
+ ]),
+ //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
+ ])
+ ]);
+ }
+
+// read configuration from file
+ readConfigurationFile{
+ arg configFile;
+ var configFromFile, reader, path;
+ path = PathName.new(configFile.asString);
+ if(path.isFile,{
+ postln("Reading configuration file: "++configFile);
+ try{
+ configFromFile = (configFile.asString).parseYAMLFile;
+ configFromFile.keysValuesDo({|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;
+ });
+ }{
+ ("Failed parsing the file: "++configFile).error;
+ ("Make sure its JSON syntax is valid!").error;
+ ^false;
+ };
+ }, {
+ error("File not readable:"++configFile.asString);
+ ^false;
+ });
+ }
+
+ //TODO: add writeConfigurationFile
+
+ // 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;
+ });
+ if(hasToBeInteger.includes(control.asSymbol),{
+ ^control.asSymbol.asSpec.constrain(value.asInteger).asInteger;
+ });
+ if(hasToBeBool.includes(control.asSymbol),{
+ switch(value,
+ 0,{^false},
+ 1,{^true},
+ "true",{^true},
+ "false",{^false}
+ );
+ });
+ },{
+ ^nil;
+ });
+ }
+
+ //get a setting value as correct type
+ getSettingValue{
+ arg setting, value;
+ if(defaultConfig.includesKey(setting.asSymbol),{
+ if(hasToBeString.includes(setting.asSymbol),{
+ ^value.asString;
+ });
+ if(hasToBeInteger.includes(setting.asSymbol),{
+ ^value.asInteger;
+ });
+ },{
+ ^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);
+ });
+ });
+ });
+ // 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);
+ });
+ });
+ // 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);
+ });
+ });
+ ^true;
+ }
+
+ //print the current config
+ showConfig{
+ postln("Configuration is: ");
+ config.keysValuesDo{|key, value|
+ 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);
+ },{
+ ^false;
+ });
+ }
+
+ // 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]);
+ ^true;
+ },{
+ ^false;
+ });
+ }
+
+}