BowelyzerConfig{ const 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" -> 9999, "controls" -> Dictionary.with(*[ "left" -> defaultControls, "right" -> defaultControls ]) ]); ^defaultConfig; } // 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| if(config.includesKey(key),{ config.put(key,value); }); }); this.validateConfig; }{ ("Failed parsing the file: "++configFile).error; ("Make sure its JSON syntax is valid!").error; }; }, { error("File not readable:"++configFile.asString); }); } //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; }); // check if the strings are associated with the correct keys if(hasToBeString.includes(key.asSymbol) && value.isKindOf(String).not, { config.put(key, value.asString); }); // 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); }); // 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; }); //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); }); }); }); }); }); //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); }); }); }); }); // 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. }); }); }); //TODO: add defaultControls for channels that have none //TODO: fill non-existing keys with default values (in case config file is incomplete) }); } //print the current config showConfig{ 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++")"); }); }; } 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; }); } }