BowelyzerConfig{ var 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(*[ "channelOffset" -> [0, 0], "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 == "channelOffset") && (value.size >= 1) && value[0].isKindOf(Integer).not,{ for(0, value.size-1, {|i| config.at(key)[i] = value[i].asInteger; }); }); 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); }); }); }); }); }); }); // if not completely broken, fix stuff if(broken,{ error("There were errors. Reverting to default config."); config = this.createDefaultConfig; },{ postln("Fixing stuff"); // zero-pad channel offsets if(config.at("names").size > config.at("channelOffset").size,{ var difference = config.at("names").size - config.at("channelOffset").size; config.at("channelOffset").asArray = config.at("channelOffset")++Array.fill(difference, {arg i; i*0}); }); //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++")"); }); }; } }