// title: Launchpad // author: olafhochherz // description: // This are classes i wrote to use the Novation Launchpad in a GUI like manner ... because i never programmed a GUI before, they might be a little bit esoteric. use examples will follow ... // code: Launchpad{ classvar <>midiout; classvar <>pads;//2DARRAY classvar nonr,nofr,cor;//MIDI RESPONDER FUNCTIONS *initClass{ if(Launchpad.initMIDI.notNil,{ Launchpad.setupLP; Launchpad.pads=Array2D.fromArray(9,9, Array.fill(81,{|i|LPPad.new((i/9).asInteger,i%9)})); }) } *initMIDI{ var non,nof,co,midisource,mididestination; MIDIClient.init; midisource=MIDIClient.sources.detect{|a|a.name.contains("Launchpad")}; if(midisource.notNil,{ mididestination=MIDIClient.destinations.detect{|a|a.name.contains("Launchpad")}; MIDIIn.connect(0, midisource.uid); midiout=MIDIOut(0, mididestination.uid); MIDIOut.connect(0, mididestination.uid); non={|src,chan,num,val| var x=num%16; var y=(num-x)/16; //[\on,src,chan,num,val].postln; //r{ if(val===127,{ pads.at(x,y+1).responseOn; },{ pads.at(x,y+1).responseOff; }); //}.play; }; nonr=NoteOnResponder(non,midisource.uid,swallowEvent:true); nof={|src,chan,num,val| var x=num%16; var y=(num-x)/16; //[\off,src,chan,num,val].postln; //r{ pads.at(x,y+1).responseOff; //}.play; }; nofr=NoteOffResponder(non,midisource.uid,swallowEvent:true); co={|src,chan,num,val| var x=num-104; //[\co,src,chan,num,val].postln; //r{ if((val%2)===1,{ pads.at(x,0).responseOn; },{ pads.at(x,0).responseOff; }) //}.play; }; cor=CCResponder(co,midisource.uid,swallowEvent:true); ^ 1; },{ "Launchpad Not available".postln; ^nil; }) } *resetLED{ //reset pads.do{|i|i.led=LPLED.black}; midiout.control(0,0,0); } *xyMode{ midiout.control(0,0,1); } *drumMode{ midiout.control(0,0,2); } *setContrast{|num=1,denum=6| midiout.control(0,30,((16*(num-1))+(denum-3))); } *setupLP{ this.resetLED; this.xyMode; this.setContrast; } *xyMIDINode{|x,y| ^(x+(y*16)) } *pos2CC{|pos| ^(104+pos) } *getRange{|fx,fy,tx,ty| var ttx,tty; //ttx=if(fx>=(tx?0),{fx+1},{tx+1}); //tty=if(fy>=(ty?0),{fy+1},{ty+1s}); ttx=tx?fx; tty=ty?fy; ^LPRange(pads.copyRange(fx,fy,ttx,tty)); } //simply set LEDs *setLED{|x,y,val| //\setLED.postln; if(y==0,{ Launchpad.setTopLED(x,y,val); },{ Launchpad.setMatrixLED(x,y,val); }) } *setMatrixLED{|x,y,val| midiout.noteOn(0, Launchpad.xyMIDINode(x,y-1), val); } *setTopLED{|x,y,val| midiout.control(0, Launchpad.pos2CC(x), val); } *setPlayLED{|x,y,val| midiout.noteOn(0, Launchpad.xyMIDINode(8,y), val); } //set LEDs for initialisation *setAllLED{|ledarray| "todo".postln; } //doublebuffering? //flashing? } LPRange{ var <>pads;//2DARRAY *new{|b| ^super.new.init(b); } init{|b| pads=b; } getRange{|fx,fy,tx,ty| var ttx,tty; ttx=tx?fx; tty=ty?fy; ^LPRange(pads.copyRange(fx,fy,ttx,tty)); } parent_{|o| pads.do{|i|i.parent=o}; } led_{|l| pads.do{|i|i.led=l}; } } LPPad{ var <>x,<>y; var led; var <>pastparent; var pastled; var green,<>red,clear,copy; *new{|green=0,red=0,clear=1,copy=1| ^super.new.init(green,red,clear,copy); } *black{^this.new(0)} *green{|s=3|s=s.min(3);^this.new(s)} *red{|s=3|s=s.min(3);^this.new(0,s)} *amber{|s=3|s=s.min(3); ^this.new(s,s)} *yellow{|s=2|s=s.min(2);^this.new(s+1,s)} *orange{|s=2|s=s.min(2);^this.new(s,s+1)} next{ var g,r; var ns=4.collect{|g| 4.collect{|r|[g,r]}}.flatten; #g,r=ns[(ns.indexOfEqual([green,red])+1)%ns.size]; green=g; red=r; } init{|gr,re,cl,co| green=gr; red=re; clear=cl; copy=co; } vel{ ^( (2.pow(4)*green.value.round.asInteger) + (2.pow(3)*clear) + (2.pow(2)*copy) + (red.value.round.asInteger) ); } } LPButton{//eine typische abstraktion var <>parent; var <>onFunction,<>offFunction; *new {|onFunction,offFunction| ^super.new.padinit(onFunction,offFunction); } padinit{| onf, offf| onFunction= onf; offFunction= offf; } responseOn{|pad| onFunction.value(pad); } responseOff{|pad| offFunction.value(pad); } initLED{ parent.initLED; } } LPWidget{//lpwidgets sind moegliche parents fuer ranges var <>range; var onfront; var onhide; var isfront; *new{|range,onfront,onhide| ^super.new.winit(range,onfront,onhide); } winit{|r,of,oh| range=r; onfront=of; onhide=oh; } front{|r| onfront.value(this); isfront=true; range=r?range; this.initParent; this.initLED; } hide{|blacking=true| onhide.value(blacking); isfront!?{ isfront=nil; range.pads.do{|p|p.unparent(blacking)}; }; } responseOn{|pad| } responseOff{|pad| } initParent{ range.parent=this; } initLED{ //range.led=...; } } LPBlackWidget : LPWidget{ var <>led; *new {|range,onfront,onhide| ^super.new(range,onfront,onhide).bwinit; } bwinit{ led=LPLED.black; } initLED{ range.led=led; } } LPLEDButton : LPWidget{ var <>button; var <>onled; var <>offled; var state; *new {|button,onled,offled,range,onfront,onhide| ^super.new(range,onfront,onhide).ledpadinit(button,onled,offled); } ledpadinit{|bt,onl,offl,r| state=0; button=bt??{LPButton()}; button.parent=this; onled=onl??{LPLED.red(2)}; offled=offl??{LPLED.green(2)}; } initLED{ range.led=offled; } responseOn{|pad| button.responseOn(pad); range.led=onled; } responseOff{|pad| button.responseOff(pad); range.led=offled; } } LPLEDSwitch : LPWidget{ var functions; var leds; var onoff; var func; var <>state; var getstatefunc; var size; var oncolor; var offcolor; *new {|func,getstatefunc,oncolor,offcolor,range,onfront,onhide| //"LPVFader is not tested".postln; ^super.new(range,onfront,onhide).finit(func,getstatefunc,oncolor,offcolor); } finit{|f,g,onc,ofc| getstatefunc=g; func=f; oncolor=onc; offcolor=ofc; } updateState{|padpos| onoff=true; statevarroutine.stop; statevarroutine=r{ while({onoff},{ func.value(this,padpos); this.updateLED; 0.2.wait; }) }.play; } initParent{ size=range.pads.size; state=getstatefunc.value(this); range.pads.do{|item,i| item.parent=LPButton({this.updateState(i)},{onoff=false}); item.parent.parent=this; }; } initLED{ this.updateLED; } updateLED{ var ledstate,lls; ledstate=state*size; range.pads.do{|item,i| if(ledstate>((size-1)-i),{ lls=ledstate-((size-1)-i); ledstate=ledstate-lls; item.led=LPLED.perform(oncolor,lls*3); //{item.led=LPLED.green(lls*3)}, //{item.led=LPLED.red(lls*3)} //); },{ item.led=LPLED.perform(offcolor,1); }) } } } LPParameterFader :LPFader{ var rand; *new{|parameter,oncolor,offcolor,range,onfront,onhide,rand| ^super.new(nil,nil,nil,nil,range,onfront,onhide).pfinit(parameter,oncolor,offcolor,rand); } pfinit{|parameter,oncolor,offcolor,r| rand=r; this.finit({|fader,padpos| var state=parameter.asSpec.map(fader.state+[0.1,0.02,0.005,rand.postln.value.postln.bilinrand.postln(\rand), -0.005, -0.02, -0.1][padpos]); fader.state=parameter.asSpec.unmap(state); LPSetupNormalGlobals.synth.value.node.set(parameter,state.postln(" "++parameter++":")); },{ var value=LPSetupNormalGlobals.synth.value.node.getKeysValues.detect{|i|i[0]===parameter}[1]; parameter.asSpec.unmap(value); },oncolor,offcolor); } } LPSpecFader : LPWidget{ var statevarroutine; var onoff; var func; var state; var spec; var size; *new {|func,spec,range,onfront,onhide| //"LPVFader is not tested".postln; ^super.new(range,onfront,onhide).finit(func,spec); } finit{|f,s| spec=s; state=spec.unmap(spec.default); func=f; } updateState{|padpos| var ud = ((size - 1) / 2) - padpos; var specstep=if(spec.step==0,{spec.range/512},{spec.step}); var step=specstep/spec.range; onoff=true; statevarroutine.stop; statevarroutine=r{ while({onoff},{ case //{ud > 0}{state=spec.unmap(spec.map(state)+(step*2.pow(ud)))} {ud > 0}{state=((state)+(step*2.pow(ud))).min(1)} {ud == 0}{state.postln;} //{ud < 0}{state=spec.unmap(spec.map(state)-(step*2.pow(ud.abs)))} {ud < 0}{state=((state)-(step*2.pow(ud.abs))).max(0)} ; this.updateLED; func.value(spec.map(state),this); (0.1/ud.abs).wait; }) }.play; } initParent{ size=range.pads.size; range.pads.do{|item,i| item.parent=LPButton({this.updateState(i)},{onoff=false}); item.parent.parent=this; }; } initLED{ this.updateLED; } updateLED{ var ledstate,lls; var green=true; ledstate=state*size; range.pads.do{|item,i| if(ledstate>((size-1)-i),{ lls=ledstate-((size-1)-i); ledstate=ledstate-lls; if(green, {item.led=LPLED.green(lls*3)}, {item.led=LPLED.red(lls*3)} ); },{ item.led=LPLED.black; }) } } } LPSwitchArray : LPWidget{ var <>functions; var <>onleds; var <>ofleds; var