BCF { classvar midiChannel=0, <>midiOut, proxyspace; var func, ((65..80)++(89..92)), \slider -> (81..88), // rotary groups 1-4 \r1 -> (1..8), \r2 -> (9..16), \r3 -> (17..24), \r4 -> (25..32), // rotary push groups 1-4 \p1 -> (33..40), \p2 -> (41..48), \p3 -> (49..56), \p4 -> (57..64) ] } *new {|type=\slider, num=1, spec, function| var instance; instance = instances[type, num]; if (instance.isNil) { instance = super.newCopyArgs(type,num,ccMap[type][num-1]).spec_(spec ?? \midi).func_(function).init; instances[type, num] = instance; } { if (spec.notNil) { instance.spec = spec }; if (function.notNil) { instance.func = function } }; ^instance } init { routine = Routine { var event, newValue; loop { event = MIDIIn.waitControl(nil, midiChannel, midiController); midiValue = event.ctlval; newValue = spec.map(midiValue / 127.0); if (newValue != value) { func.value(value = newValue, this) } } }.play } spec_ {|newSpec| spec = newSpec.asSpec; this.value = spec.default } set {|newValue| if (newValue != value) { this.value_(newValue); func.value(newValue, this) } } value_ {|newValue| var ccValue; ccValue = (spec.unmap(value = newValue)*127).asInteger; if (midiOut.notNil and: { ccValue != midiValue }) { midiOut.control(midiChannel, midiController, ccValue) }; midiValue = ccValue } connect {|key, argName| key.class.switch( Symbol, { this.func = {|val| BCF.proxyspace[key].set(argName, val) } }, NodeProxy, { this.func = {|val| key.set(argName, val) } }, Synth, { this.func = {|val| key.set(argName, val) } } ) } storeArgs { ^[type,num,Spec.specs.findKeyForValue(spec) ?? spec,func] } free { routine.stop; instances.removeAt(type,num); super.free; } }