From 242b19ad70677a9954365cbd2aaa700e06f63277 Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Thu, 17 Oct 2013 16:48:43 -0700 Subject: [PATCH] Add generic grid API --- MarkSlee.pde | 21 ++++-- _Internals.pde | 2 +- _MIDI.pde | 175 +++++++++++++++++++++++++++++++++++++---------- code/GLucose.jar | Bin 24978 -> 25000 bytes code/HeronLX.jar | Bin 67726 -> 67726 bytes 5 files changed, 154 insertions(+), 44 deletions(-) diff --git a/MarkSlee.pde b/MarkSlee.pde index b90a822..649d61c 100644 --- a/MarkSlee.pde +++ b/MarkSlee.pde @@ -54,7 +54,7 @@ class MidiMusic extends SCPattern { } } - public synchronized boolean noteOnReceived(Note note) { + public synchronized boolean noteOn(Note note) { if (note.getChannel() == 0) { for (LightUp light : allLights) { if (light.isAvailable()) { @@ -74,7 +74,7 @@ class MidiMusic extends SCPattern { return true; } - public synchronized boolean noteOffReceived(Note note) { + public synchronized boolean noteOff(Note note) { if (note.getChannel() == 0) { LightUp light = lightMap.get(note.getPitch()); if (light != null) { @@ -118,6 +118,7 @@ class Pulley extends SCPattern { addParameter(sz); addParameter(beatAmount); trigger(); + } private void trigger() { @@ -138,6 +139,7 @@ class Pulley extends SCPattern { if (reset.click()) { trigger(); } + if (isRising) { // Fucking A, had to comment this all out because of that bizarre // Processing bug where some simple loop takes an absurd amount of @@ -169,6 +171,15 @@ class Pulley extends SCPattern { } } } + + // A little silliness to test the grid API + for (int i = 0; i < 7; ++i) { + for (int j = 0; j < 8; ++j) { + int gi = (int) constrain(j * NUM_DIVISIONS / 8, 0, NUM_DIVISIONS-1); + float b = 1 - 4.*abs((6-i)/7. - gravity[gi].getValuef() / model.yMax); + midiEngine.grid.setState(i, j, (b < 0) ? 0 : 1); + } + } float fPos = 1 - lx.tempo.rampf(); if (fPos < .2) { @@ -391,7 +402,7 @@ class BouncyBalls extends SCPattern { } } - public boolean noteOnReceived(Note note) { + public boolean noteOn(Note note) { int pitch = (note.getPitch() + note.getChannel()) % NUM_BALLS; balls[pitch].bounce(note.getVelocity()); return true; @@ -821,13 +832,13 @@ public class PianoKeyPattern extends SCPattern { return base[index % base.length]; } - public boolean noteOnReceived(Note note) { + public boolean noteOn(Note note) { LinearEnvelope env = getEnvelope(note.getPitch()); env.setEndVal(min(1, env.getValuef() + (note.getVelocity() / 127.)), getAttackTime()).start(); return true; } - public boolean noteOffReceived(Note note) { + public boolean noteOff(Note note) { getEnvelope(note.getPitch()).setEndVal(0, getReleaseTime()).start(); return true; } diff --git a/_Internals.pde b/_Internals.pde index e568c4e..3aa9f3a 100644 --- a/_Internals.pde +++ b/_Internals.pde @@ -52,6 +52,7 @@ LXPattern[] patterns; MappingTool mappingTool; PandaDriver[] pandaBoards; MidiEngine midiEngine; +GridController gridController; // Display configuration mode boolean mappingMode = false; @@ -99,7 +100,6 @@ LXPattern[] _rightPatterns(GLucose glucose) { } return rightPatterns; } - void logTime(String evt) { int now = millis(); diff --git a/_MIDI.pde b/_MIDI.pde index 323efee..d6e90bf 100644 --- a/_MIDI.pde +++ b/_MIDI.pde @@ -22,6 +22,7 @@ interface MidiEngineListener { class MidiEngine { + public final GridController grid; private final List listeners = new ArrayList(); private final List midiControllers = new ArrayList(); @@ -41,17 +42,17 @@ class MidiEngine { private int activeDeckIndex = 0; public MidiEngine() { - - midiControllers.add(midiQwertyKeys = new SCMidiInput(SCMidiInput.KEYS)); - midiControllers.add(midiQwertyAPC = new SCMidiInput(SCMidiInput.APC)); + grid = new GridController(this); + midiControllers.add(midiQwertyKeys = new SCMidiInput(this, SCMidiInput.KEYS)); + midiControllers.add(midiQwertyAPC = new SCMidiInput(this, SCMidiInput.APC)); for (MidiInputDevice device : RWMidi.getInputDevices()) { if (device.getName().contains("APC")) { - midiControllers.add(new APC40MidiInput(device).setEnabled(true)); + midiControllers.add(new APC40MidiInput(this, device).setEnabled(true)); } else if (device.getName().contains("SLIDER/KNOB KORG")) { - midiControllers.add(new KorgNanoKontrolMidiInput(device).setEnabled(true)); + midiControllers.add(new KorgNanoKontrolMidiInput(this, device).setEnabled(true)); } else { boolean enabled = device.getName().contains("KEYBOARD KORG") || device.getName().contains("Bus 1 Apple"); - midiControllers.add(new SCMidiInput(device).setEnabled(enabled)); + midiControllers.add(new SCMidiInput(this, device).setEnabled(enabled)); } } for (MidiOutputDevice device : RWMidi.getOutputDevices()) { @@ -65,6 +66,14 @@ class MidiEngine { return this.midiControllers; } + public Engine.Deck getFocusedDeck() { + return lx.engine.getDeck(activeDeckIndex); + } + + public SCPattern getFocusedPattern() { + return (SCPattern) getFocusedDeck().getActivePattern(); + } + public MidiEngine setFocusedDeck(int deckIndex) { if (this.activeDeckIndex != deckIndex) { this.activeDeckIndex = deckIndex; @@ -75,10 +84,6 @@ class MidiEngine { return this; } - public Engine.Deck getFocusedDeck() { - return lx.engine.getDeck(activeDeckIndex); - } - public boolean isQwertyEnabled() { return midiQwertyKeys.isEnabled() || midiQwertyAPC.isEnabled(); } @@ -99,6 +104,8 @@ public class SCMidiInput extends AbstractScrollItem { private final int mode; private int octaveShift = 0; + protected final MidiEngine midiEngine; + class NoteMeta { int channel; int number; @@ -122,13 +129,15 @@ public class SCMidiInput extends AbstractScrollItem { return this; } - SCMidiInput(MidiInputDevice d) { + SCMidiInput(MidiEngine midiEngine, MidiInputDevice d) { + this.midiEngine = midiEngine; mode = MIDI; d.createInput(this); name = d.getName().replace("Unknown vendor",""); } - SCMidiInput(int mode) { + SCMidiInput(MidiEngine midiEngine, int mode) { + this.midiEngine = midiEngine; this.mode = mode; switch (mode) { case APC: @@ -249,10 +258,6 @@ public class SCMidiInput extends AbstractScrollItem { return this; } - protected SCPattern getFocusedPattern() { - return (SCPattern) midiEngine.getFocusedDeck().getActivePattern(); - } - private boolean logMidi() { return (uiMidi != null) && uiMidi.logMidi(); } @@ -272,10 +277,12 @@ public class SCMidiInput extends AbstractScrollItem { return; } if (logMidi()) { - println(getLabel() + " :: Controller :: " + cc.getCC() + ":" + cc.getValue()); + println(getLabel() + " :: Controller :: " + cc.getChannel() + " :: " + cc.getCC() + ":" + cc.getValue()); } - if (!getFocusedPattern().controllerChangeReceived(cc)) { - handleControllerChange(cc); + if (!handleGridControllerChange(cc)) { + if (!midiEngine.getFocusedPattern().controllerChange(cc)) { + handleControllerChange(cc); + } } } @@ -286,8 +293,10 @@ public class SCMidiInput extends AbstractScrollItem { if (logMidi()) { println(getLabel() + " :: Note On :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity()); } - if (!getFocusedPattern().noteOnReceived(note)) { - handleNoteOn(note); + if (!handleGridNoteOn(note)) { + if (!midiEngine.getFocusedPattern().noteOn(note)) { + handleNoteOn(note); + } } } @@ -298,20 +307,21 @@ public class SCMidiInput extends AbstractScrollItem { if (logMidi()) { println(getLabel() + " :: Note Off :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity()); } - if (!getFocusedPattern().noteOffReceived(note)) { - handleNoteOff(note); + if (!handleGridNoteOff(note)) { + if (!midiEngine.getFocusedPattern().noteOff(note)) { + handleNoteOff(note); + } } } // Subclasses may implement these to map top-level functionality - protected void handleProgramChange(ProgramChange pc) { - } - protected void handleControllerChange(rwmidi.Controller cc) { - } - protected void handleNoteOn(Note note) { - } - protected void handleNoteOff(Note note) { - } + protected boolean handleGridNoteOn(Note note) { return false; } + protected boolean handleGridNoteOff(Note note) { return false; } + protected boolean handleGridControllerChange(rwmidi.Controller cc) { return false; } + protected void handleProgramChange(ProgramChange pc) {} + protected void handleControllerChange(rwmidi.Controller cc) {} + protected void handleNoteOn(Note note) {} + protected void handleNoteOff(Note note) {} } public class APC40MidiInput extends SCMidiInput { @@ -319,8 +329,42 @@ public class APC40MidiInput extends SCMidiInput { private boolean shiftOn = false; private LXEffect releaseEffect = null; - APC40MidiInput(MidiInputDevice d) { - super(d); + APC40MidiInput(MidiEngine midiEngine, MidiInputDevice d) { + super(midiEngine, d); + } + + private class GridPosition { + public final int row, col; + GridPosition(int r, int c) { + row = r; + col = c; + } + } + + private GridPosition getGridPosition(Note note) { + int channel = note.getChannel(); + int pitch = note.getPitch(); + if (channel < 8) { + if (pitch >= 53 && pitch <=57) return new GridPosition(pitch-53, channel); + else if (pitch == 52) return new GridPosition(5, channel); + } + return null; + } + + protected boolean handleGridNoteOn(Note note) { + GridPosition p = getGridPosition(note); + if (p != null) { + return midiEngine.grid.gridPressed(p.row, p.col); + } + return false; + } + + protected boolean handleGridNoteOff(Note note) { + GridPosition p = getGridPosition(note); + if (p != null) { + return midiEngine.grid.gridReleased(p.row, p.col); + } + return false; } protected void handleControllerChange(rwmidi.Controller cc) { @@ -339,7 +383,7 @@ public class APC40MidiInput extends SCMidiInput { parameterIndex = 8 + (number-16); } if (parameterIndex >= 0) { - List parameters = getFocusedPattern().getParameters(); + List parameters = midiEngine.getFocusedPattern().getParameters(); if (parameterIndex < parameters.size()) { parameters.get(parameterIndex).setValue(cc.getValue() / 127.); } @@ -465,15 +509,15 @@ public class APC40MidiInput extends SCMidiInput { class KorgNanoKontrolMidiInput extends SCMidiInput { - KorgNanoKontrolMidiInput(MidiInputDevice d) { - super(d); + KorgNanoKontrolMidiInput(MidiEngine midiEngine, MidiInputDevice d) { + super(midiEngine, d); } protected void handleControllerChange(rwmidi.Controller cc) { int number = cc.getCC(); if (number >= 16 && number <= 23) { int parameterIndex = number - 16; - List parameters = getFocusedPattern().getParameters(); + List parameters = midiEngine.getFocusedPattern().getParameters(); if (parameterIndex < parameters.size()) { parameters.get(parameterIndex).setValue(cc.getValue() / 127.); } @@ -502,7 +546,7 @@ class KorgNanoKontrolMidiInput extends SCMidiInput { } } -class APC40MidiOutput implements LXParameter.Listener { +class APC40MidiOutput implements LXParameter.Listener, GridOutput { private final MidiEngine midiEngine; private final MidiOutput output; @@ -531,6 +575,7 @@ class APC40MidiOutput implements LXParameter.Listener { d.addListener(deckListener); } resetParameters(); + midiEngine.grid.addOutput(this); } private void resetParameters() { @@ -557,6 +602,11 @@ class APC40MidiOutput implements LXParameter.Listener { while (i < 12) { sendKnob(i++, 0); } + for (int row = 0; row < 7; ++row) { + for (int col = 0; col < 8; ++col) { + setGridState(row, col, 0); + } + } } private void resetEffectParameters() { @@ -610,4 +660,53 @@ class APC40MidiOutput implements LXParameter.Listener { ++i; } } + + public void setGridState(int row, int col, int state) { + if (col < 8) { + if (row < 5) output.sendNoteOn(col, 53+row, state); + else if (row == 6) output.sendNoteOn(col, 52, state); + } + } +} + +interface GridOutput { + public static final int OFF = 0; + public static final int GREEN = 1; + public static final int GREEN_BLINK = 2; + public static final int RED = 3; + public static final int RED_BLINK = 4; + public static final int YELLOW = 5; + public static final int YELLOW_BLINK = 6; + public static final int ON = 127; + + public void setGridState(int row, int col, int state); } + +class GridController { + private final List outputs = new ArrayList(); + + private final MidiEngine midiEngine; + + GridController(MidiEngine midiEngine) { + this.midiEngine = midiEngine; + } + + public void addOutput(GridOutput output) { + outputs.add(output); + } + + public boolean gridPressed(int row, int col) { + return midiEngine.getFocusedPattern().gridPressed(row, col); + } + + public boolean gridReleased(int row, int col) { + return midiEngine.getFocusedPattern().gridReleased(row, col); + } + + public void setState(int row, int col, int state) { + for (GridOutput g : outputs) { + g.setGridState(row, col, state); + } + } +} + diff --git a/code/GLucose.jar b/code/GLucose.jar index 91dd10571d1b469e488b39317fbc639d135e1c6c..4bbff0b4aee0933ebe94c443af570c4963d62086 100755 GIT binary patch delta 1398 zcmbPqm~q8nM&1B#W)?061`Y-WsnWoSyc*1sK+2*IOr2B!Q*V@+pO*wq7Gl%}(+-SU zAmPcmjGAEnOrSW3f1FVl%>T})2j*)q-3QT|xtOatz>E&=pNwEe3-4D5qlTZ62`the z=m`-yBUA!W>?|F@0+yO5Zx2!PK+%Z}ETXF61W}x&*$z?jMOTv@ETv#t2a#H6*3AkQ z(YH~Ahy>f3K@|5m-UAyrIoIV7Sc#EqEktIM=MrWipp%`iHXGkq$;7}gg_VIpo?&us zf@1yJ;M+l$14Lrgd3kdW?-dDfIT(E_YH`K#j_FzfJi5w#hhGF|@C)#Gs&k%N|ATek zk4HQ`&HR1m|F!OaXz|K!VaUQ(^-l%gZdR||eD3D-n*V>FYBv;4N#4Pu+n2W_`Gu3) zj>wIRpK=v(#YNSdPkEq({4#;ne9nCdSiIX>n8*kgdenJOTBt;%?`Wt7lyB* zYmaf{&Rdv!cx}<_h5H*@8EfO#w79G59_LynF)8Zu%P-z+I~`ow%I34PUN5t4b9_4? zB(Zqr^X{fQ8zp8;HUFIUwsmJtF{`ldjmv3E&&gTw=(5cVwPuPg&{e;2SZMXU(AW<< z-hb9oT_M{2GMuM=RfpYkGxNg*R|=Wq$|QD~9piD@w-gz#tj>`Crd;=p7(l2T>XRB-@=*`bWAz+ zzJ3-Re4j@rT0iIrk4yh9zO9oSH*ME2f820uO+nAnnuvGPIyB1ji?^@bV>+?RU)1E^ z-Ta6;horge$|g2pj}tX@b4~1L@M&f*i+>b-q%UKU`zB$llePPN?_NB8`t=^Absb?x zbS{0p^jfL6;c1BF(&Jse>qRfuU%S#)aPCRqujRX5mQR@=@^(pVP|1`W8KY85);7jZ z$2t@z`lfEWoiJZ9b(3`mhx^jj&k0|UU1|NA zZ$h4nz>$+V6$R@fc+v~k`Mpxn_6T~CEGuoxCVlwle}k6{We+9ZDCx;IKGcWhY7Pca z4%7_Lhvb3iKxs&>s0_1WWCmvV$=kv$KzU&D$8dWvZ5&|$rqd#f!I^nZgf*DH6=4My z7mu`ric3$&VPJZ7vh`U%5Ja|!Z`Q)esh)d@tNHZEu-kG4I0Fr;}ko5B&3j;$MuzXZvK!7EUDS?x- t0#zn!Ch~w4KD`}R`~xW8!N|a%icrU}q_HV%az~i@OxA#jyc*0IK+2+z8N@rOz?=Z2-YA2p$wG|UVA_FE z3nV)^mr)bUp9vK|&ZrCKe`nMK%WE**2hp3kn5#L!j1KOfj9^9!?^g(;hM$oMEYcw8 z2@yFXR02`#EFHlDmYOGT4^i_#(TNQ#qN?EpQJkgO4pH+(SCbtqrC?eIky>ch%?cLL zw^4+M1lyWH6!$pZ0~7QK^waGudxsp&yHKda_CYjM3>UC~e|x>sY>3pX!U z{!3hHAvQ;rG`VZZJ?5Oc@{wkV==l$G7A`-!H`FIF>L`yFSDNUa1;6+rMU6RjywCFf z^{DZHl+O#o7eTmtb+~C_>IqupQ`y@N+x0T4fmTP|UCLs5i;8saN ziPJ|vv_>mEY7IWiyUcTCN6@M-cibM=9A3w!GRv_fZScc^QW!TO30s4^ST^J}0N$~w)R$!6IXTmK&nRzmMggq#kPxg#30O!w| z2xBmv87V#aYy>!qPyP^L1y-XQX$h6H2Gh$UZNc>ONIS5+MwCC8u8ML1(|ekX!xVj=t!u`Xafe;kAki-YK& zALj$n2NgGshsf8&L(DrF4{?`tf(Kd#E=X{Y2W9CLTa5+Z85kJC7#SF3fiw&(X_T3K zK0ya;`o9EeCcnVR4S_0??Gt&xLVk(TOgUkb=Y^?@1$eWvfpjSY;Z;rshUMWP9smk4 B|7HLH diff --git a/code/HeronLX.jar b/code/HeronLX.jar index 6069116fa5100282693f58bbf58e7c6134982384..fa0173929c7238682d054769af4a02c4d73e15b1 100755 GIT binary patch delta 1402 zcmY+CYfw~W9LCRk&hDONAybJQXBZ6)1@v#srYD`mA*Ra*EjEVAX&i}emXm#m2%0fy8Wpl#mX-TuX`ko)QeVz*pa1j!KkxrN zXAcEzhXS@F--CwDrD;x0s|yB_GWFVEV9jAX&!^#eZCU&&lLZW!FYW-I7I!MOSk9d= z|C12+eigUFv+LPFo}Q})7R&at&i{8d>Z{^jYp1^54z6}abucsW6#=#+r6+*Dr8rpc zU69TC+=ZqqUY84(ehIs4X${N8>3#E|GuCc3=F|o^Zt!Py( zaNQS7i{*O^XivdDvb|_IV&p(^C(7qahMi!WZ$Hylm1cA7$+AyCd2{&{*s*diZEwY^ zWcRjvnYN(vnp(oV`*VBP(6lB~e&tKX+iK17C)hB%dgluE`gdQVcGnzrpx(2$Lhu3! zp(lEd#b^Bv>E4+vt7;NkEBBq|%ni@wa2@vxcGB}=J2PAdB=@McI&TXcCtV8O&<0S6>|QdM59J@c4`?3T zpjuL;Es~C(DX9;d9V458l+lNQ>d{AmA4eYnmXGoD=f^6by2dJj zyz#9-!#L;u8fRwi#6}cfn&1)_CsV^ zG`%%5UZ{f#Ju}>8WsKtoVqEw47++S-EH`^=mgC`BEO^8te86#mfg=%tgRD(LAjW!6 z2;9(`6#~sxg)VTW^{Fn9Va+jAf3>0f?+jI6ViSJ&^)`Vctt&R=ueU26x2xPSyTE3a c*P-+&hu8r<&lGr_ZmW zWt8a{`t>cjZGkfwhc@LyjNf0oGthRhdt0ndmPN-dPL z$Bq9a#Jyj|o$zdWHjt<1s)2>F?VRKPosIg+xYyF5Z?l1`98n$2OngOv%}MDA;BP5* z)_dkwQd{S9x76Vche%Eo{-WCPRMZO~%`5$?_#wF}rHVGWPm*U8Z)`9J8a|y|+T} z0tulfyN}0beRk>Eku0lf5?dlFjd(WbFj2WPHDz zEdQa8v#yKk6YTjZi}l8z2e{MW4uksbPA~a)R}Sk7E+*k z#lWr`9B5T)TA^s@0eB5VJg9SsDNBZ_s)x(qwGVFsQbryDsz)9JejIreSUSqn>qaY} zI!7ykys<4n!x-oO8e?Ye_y!bT8s`$1##vl2LDWp}pl_7AWs>UY$#M*QJ<0RZrnYf> zYAf)^6us5c9;kx~-P7DCIFIRy;!~g&Q -- 2.34.1