From 830f1d296e402a89c6146e4183e1a2490ca64406 Mon Sep 17 00:00:00 2001 From: David Runge Date: Sun, 19 Jun 2016 19:33:36 +0200 Subject: Bowelyzer.sc, BowelyzerAnalyzer.sc, BowelyzerConfig.sc, BowelyzerOSCHub.sc: Moving from String based identifiers to Symbols. Further refining the configuration parsing and validation process. darmstadt.json: Moving names into inputs Dictionary, holding channel number (offsets are not required/feasible) and name. --- Bowelyzer.sc | 17 ++- BowelyzerAnalyzer.sc | 52 ++++--- BowelyzerConfig.sc | 402 +++++++++++++++++++++++++++++---------------------- BowelyzerOSCHub.sc | 14 +- darmstadt.json | 34 ++--- 5 files changed, 282 insertions(+), 237 deletions(-) diff --git a/Bowelyzer.sc b/Bowelyzer.sc index 5708368..3d01b1a 100644 --- a/Bowelyzer.sc +++ b/Bowelyzer.sc @@ -1,6 +1,6 @@ Bowelyzer{ - var gui, Synth( name, [ - sendReplyFreq: controls.at("sendReplyFreq"), - amplitudeAttackTime: controls.at("amplitudeAttackTime"), - amplitudeReleaseTime: controls.at("amplitudeReleaseTime"), - hfHainsworth: controls.at("hfhainsworth"), - hfFoote: controls.at("hfFoote"), - hfThreshold: controls.at("hfThreshold"), - hfWaitTime: controls.at("hfWaitTime"), - pitchInitFreq: controls.at("pitchInitFreq"), - pitchMinFreq: controls.at("pitchMinFreq"), - pitchMaxFreq: controls.at("pitchMaxFreq"), - pitchExecFreq: controls.at("pitchExecFreq"), - pitchMaxBinsPerOctave: controls.at("pitchMaxBinsPerOctave"), - pitchMedian: controls.at("pitchMedian"), - pitchAmpThreshold: controls.at("pitchAmpThreshold"), - pitchPeakThreshold: controls.at("pitchPeakThreshold"), - pitchDownSample: controls.at("pitchDownSample") + sendReplyFreq: controls.at(\sendReplyFreq), + amplitudeAttackTime: controls.at(\amplitudeAttackTime), + amplitudeReleaseTime: controls.at(\amplitudeReleaseTime), + hfHainsworth: controls.at(\hfhainsworth), + hfFoote: controls.at(\hfFoote), + hfThreshold: controls.at(\hfThreshold), + hfWaitTime: controls.at(\hfWaitTime), + pitchInitFreq: controls.at(\pitchInitFreq), + pitchMinFreq: controls.at(\pitchMinFreq), + pitchMaxFreq: controls.at(\pitchMaxFreq), + pitchExecFreq: controls.at(\pitchExecFreq), + pitchMaxBinsPerOctave: controls.at(\pitchMaxBinsPerOctave), + pitchMedian: controls.at(\pitchMedian), + pitchAmpThreshold: controls.at(\pitchAmpThreshold), + pitchPeakThreshold: controls.at(\pitchPeakThreshold), + pitchDownSample: controls.at(\pitchDownSample) ], analyzerGroup ); diff --git a/BowelyzerConfig.sc b/BowelyzerConfig.sc index 4079119..e092b25 100644 --- a/BowelyzerConfig.sc +++ b/BowelyzerConfig.sc @@ -21,25 +21,60 @@ 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 + \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; diff --git a/BowelyzerOSCHub.sc b/BowelyzerOSCHub.sc index b73e201..4a7db26 100644 --- a/BowelyzerOSCHub.sc +++ b/BowelyzerOSCHub.sc @@ -20,25 +20,25 @@ BowelyzerOSCHub{ setupNetAddressesFromConfig{ arg config; - forward = BowelyzerOSCHub.getNetAddr(config.at("forwardAddress"), config.at("forwardPort")); - hub = BowelyzerOSCHub.getNetAddr(config.at("hubAddress"), config.at("hubPort")); - synthServer = BowelyzerOSCHub.getNetAddr(config.at("synthServerAddress"), config.at("synthServerPort")); + forward = BowelyzerOSCHub.getNetAddr(config.at(\forwardAddress), config.at(\forwardPort)); + hub = BowelyzerOSCHub.getNetAddr(config.at(\hubAddress), config.at(\hubPort)); + synthServer = BowelyzerOSCHub.getNetAddr(config.at(\synthServerAddress), config.at(\synthServerPort)); } // setup OSCdef for SynthServerAddress:SynthServerPort setupSynthListener{ arg config; // listen for individual SendReply messages - config.at("names").do({|name| + config.at(\inputs).keysValuesDo({|name, input| postln("Listening for messages called '/"++name++"' coming from scsynth at "++synthServer.ip++":"++synthServer.port++"."); OSCdef.newMatching( - name.asSymbol, + name, {|msg, time, addr, recvPort| this.forwardToNetAddress(msg, time)}, - '/'++name.asSymbol, + name, synthServer, hub.port ); - OSCdef(name.asSymbol).enable; + OSCdef(name).enable; }); } diff --git a/darmstadt.json b/darmstadt.json index e9b64bf..7d19f9f 100644 --- a/darmstadt.json +++ b/darmstadt.json @@ -1,20 +1,12 @@ { -"names": [ - "left", - "right" -], -"channelOffset": [ - 0, - 0 -], -"synthServerAddress": "127.0.0.1", -"synthServerPort": 57110, -"hubAddress": "127.0.0.1", -"hubPort": 57120, -"forwardAddress": "127.0.0.1", -"forwardPort": 9999, -"controls":{ - "left": { + "synthServerAddress": "127.0.0.1", + "synthServerPort": 57110, + "hubAddress": "127.0.0.1", + "hubPort": 57120, + "forwardAddress": "127.0.0.1", + "forwardPort": 57120, + "controls":{ + "left": { "amplitudeAttackTime": 0.01, "amplitudeReleaseTime": 0.1, "hfHainsworth": 1.0, @@ -31,8 +23,8 @@ "pitchPeakThreshold": 0.5, "pitchDownSample": 1, "sendReplyFreq": 20.0 - }, - "right": { + }, + "right": { "amplitudeAttackTime": 0.01, "amplitudeReleaseTime": 0.1, "hfHainsworth": 1.0, @@ -50,5 +42,9 @@ "pitchDownSample":1, "sendReplyFreq": 20.0 } + }, + "inputs": { + "left": 0, + "right": 1 } -} \ No newline at end of file +} -- cgit v1.2.3-54-g00ecf