MIDIDevice{ var verbose, <>states, name, \type->type, \channel->channel, \msgNum->msgNum, \spec->ControlSpec.new( minval: minVal, maxval: maxVal, step: step, default: default, units: units ) ]); } connectPorts{ if(inPortNum.notNil,{ this.connectInPort(inPortNum); }); if(outPortNum.notNil,{ this.connectOutPort(outPortNum); }); } connectInPort{ arg inPortNum; if(verbose,{ postln("Connecting MIDIIn for "++name); }); inPort = MIDIIn.new(inPortNum); MIDIIn.connect(inPortNum, inPortNum); } connectOutPort{ arg outPortNum; if(verbose,{ postln("Connecting MIDIOut for "++name); }); outPort = MIDIOut.new(outPortNum); outPort.latency=0; outPort.connect(outPortNum, outPortNum); } disconnectPorts{ this.disconnectInPort(); this.disconnectOutPort(); } disconnectInPort{ if(inPort.notNil, { if(verbose,{ postln("Disconnecting MIDIIn for "++name); }); try{ MIDIIn.disconnect(inPortNum, inPortNum); } }); } disconnectOutPort{ if(outPort.notNil, { if(verbose,{ postln("Disconnecting MIDIOut for "++name); }); try{ outPort.disconnect(outPortNum, outPortNum); } }); } // TODO: this should be handled with a type of callback in MIDIStateManager connectPassThroughs{ if(passThroughs.notNil,{ if(verbose,{ postln("Setting up passthroughs for "++name); }); passThroughs.keysValuesDo({|channel, type| MIDIdef.new( key: ("passThrough_"++uid++"_"++channel).asSymbol, func: {arg ...args; if(verbose,{ ("passThrough_"++uid++"_"++channel++": "++args).postln; }); case {type==\control}{outPort.control(chan: channel, val: args[0])} {type==\bend}{outPort.bend(chan: channel, val: args[0])} {type==\midiClock}{outPort.midiClock} {type==\bend}{outPort.bend(chan: channel, veloc: args[0])} {type==\noteOn}{outPort.noteOn(chan: channel, note: args[1], veloc: args[0])} {type==\noteOff}{outPort.noteOn(chan: channel, note: args[1], veloc: args[0])} {type==\polyTouch}{outPort.polyTouch(chan: channel, note: args[1], val: args[0])} {type==\program}{outPort.program(chan: channel, num: args[0])} {type==\touch}{outPort.touch(chan: channel, val: args[0])} {type==\start}{outPort.start} {type==\stop}{outPort.stop} {type==\continue}{outPort.continue}; }, chan: channel.asInteger, // msgNum: msgNum.asInteger, msgType: type.asSymbol, srcID: uid.asInteger ); }); }); } disconnectPassThroughs{ if(passThroughs.notNil,{ if(verbose,{ postln("Cleaning up passthroughs for "++name); }); passThroughs.keysValuesDo({|channel, type| MIDIdef(("passThrough_"++uid++"_"++channel).asSymbol).free; }); }); } connectStates{ if(verbose,{ postln("Setting up states for "++name); }); Array.with(faders, potis, buttons, jogwheels, touchpads).do({|group, i| group.do({|msgNum, i| msgNum.do({|event, i| MIDIdef.new( key: ("state_"++uid++"_"++event.at(\name)++"_" ++event.at(\type)).asSymbol, func: {arg ...args; if(verbose,{ ("state_"++uid++"_"++event.at(\name)++"_"++event.at(\type)++ ": "++args).postln; }); states.update(event.at(\name), event.at(\type), args[0], event.at(\channel), event.at(\msgNum)); }, chan: event.at(\channel).asInteger, msgNum: if(event.at(\msgNum).isInteger,{event.at(\msgNum)}), msgType: event.at(\type).asSymbol, srcID: uid.asInteger, ); }); }); }); } }