From a8d55ade10d68a95188105282c2c453816b20708 Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Thu, 19 Sep 2013 18:16:15 -0700 Subject: [PATCH] Add MIDI inputs to UI, with focused pattern deck to route input --- DanHorwitz.pde | 6 +- DanUtil.pde | 2 +- MarkSlee.pde | 6 +- _Internals.pde | 169 ++++++++++++++++++++++++++++++------------ _UIFramework.pde | 4 +- _UIImplementation.pde | 103 +++++++++++++++---------- code/GLucose.jar | Bin 26511 -> 26584 bytes 7 files changed, 194 insertions(+), 96 deletions(-) diff --git a/DanHorwitz.pde b/DanHorwitz.pde index 3b7e1d6..b15bad9 100755 --- a/DanHorwitz.pde +++ b/DanHorwitz.pde @@ -17,7 +17,7 @@ public class Pong extends DPat { pChoose = addPick ("Animiation" , 0 , 3 ); } - void StartRun(int deltaMs) { cRad = xdMax*pSize.Val()/6; } + void StartRun(double deltaMs) { cRad = xdMax*pSize.Val()/6; } color CalcPoint(xyz p) { xyz v = new xyz(x.getValuef(), y.getValuef(), z.getValuef()); switch(pChoose.Cur()) { @@ -56,7 +56,7 @@ public class Noise extends DPat pSymm = addPick("Symmetry", 0, 4); pChoose = addPick("Animation", 1, 6); } - void StartRun(int deltaMs) { + void StartRun(double deltaMs) { zTime += deltaMs*(pSpeed.Val()-.5)*.002 ; zTheta += deltaMs*(pRotZ .Val()-.5)*.01 ; rtime += deltaMs; @@ -135,7 +135,7 @@ public class Play extends DPat float LastBeat=3, LastMeasure=3; int CurRandTempo = 1; - void StartRun(int deltaMs) { + void StartRun(double deltaMs) { t = lx.tempo.rampf(); a = pAmp.Val(); diff --git a/DanUtil.pde b/DanUtil.pde index b7ad56a..7bcd56a 100644 --- a/DanUtil.pde +++ b/DanUtil.pde @@ -185,7 +185,7 @@ public class DPat extends SCPattern xyz0 = new xyz(0,0,0); pSharp = addParam("Shrp", 0); - for (MidiInputDevice input : RWMidi.getInputDevices ()) { if (input.toString().contains("APC")) input .createInput (this);} + // for (MidiInputDevice input : RWMidi.getInputDevices ()) { if (input.toString().contains("APC")) input .createInput (this);} for (MidiOutputDevice output : RWMidi.getOutputDevices()) { if (midiout == null && output.toString().contains("APC")) midiout = output.createOutput(); } diff --git a/MarkSlee.pde b/MarkSlee.pde index 9804b5c..5d08a86 100644 --- a/MarkSlee.pde +++ b/MarkSlee.pde @@ -391,11 +391,7 @@ public class PianoKeyPattern extends SCPattern { PianoKeyPattern(GLucose glucose) { super(glucose); - - for (MidiInputDevice input : RWMidi.getInputDevices()) { - input.createInput(this); - } - + addParameter(attack); addParameter(release); addParameter(level); diff --git a/_Internals.pde b/_Internals.pde index d4a5fcc..e57685d 100644 --- a/_Internals.pde +++ b/_Internals.pde @@ -48,7 +48,7 @@ HeronLX lx; LXPattern[] patterns; MappingTool mappingTool; PandaDriver[] pandaBoards; -final List midiListeners = new ArrayList(); +MidiListener midiQwerty; // Display configuration mode boolean mappingMode = false; @@ -61,6 +61,7 @@ LXPattern restoreToPattern = null; UIContext[] overlays; UIPatternDeck uiPatternA; UICrossfader uiCrossfader; +UIMidi uiMidi; UIMapping uiMapping; UIDebugText uiDebugText; @@ -119,33 +120,38 @@ void setup() { } mappingTool = new MappingTool(glucose, pandaMappings); logTime("Built PandaDriver"); - + + // MIDI devices + List midiListeners = new ArrayList(); + midiListeners.add(midiQwerty = new MidiListener()); + for (MidiInputDevice device : RWMidi.getInputDevices()) { + boolean enableDevice = device.getName().contains("APC"); + midiListeners.add(new MidiListener(device).setEnabled(enableDevice)); + } + SCMidiDevices.initializeStandardDevices(glucose); + logTime("Setup MIDI devices"); + // Build overlay UI debugUI = new DebugUI(pandaMappings); overlays = new UIContext[] { - uiPatternA = new UIPatternDeck(lx.engine.getDeck(0), "PATTERN A", 4, 4, 140, 344), - uiCrossfader = new UICrossfader(4, 352, 140, 212), - new UIOutput(4, 568, 140, 106), + uiPatternA = new UIPatternDeck(lx.engine.getDeck(0), "PATTERN A", 4, 4, 140, 324), + new UIBlendMode(4, 332, 140, 86), + new UIEffects(4, 422, 140, 144), + new UITempo(4, 570, 140, 50), + new UISpeed(4, 624, 140, 50), + + new UIPatternDeck(lx.engine.getDeck(1), "PATTERN B", width-144, 4, 140, 324), + uiMidi = new UIMidi(midiListeners, width-144, 332, 140, 136), + new UIOutput(width-144, 472, 140, 106), - new UIPatternDeck(lx.engine.getDeck(1), "PATTERN B", width-144, 4, 140, 344), - new UIEffects(width-144, 352, 140, 144), - new UITempo(width-144, 498, 140, 50), - new UISpeed(width-144, 552, 140, 50), + uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86), - uiDebugText = new UIDebugText(4, height-64, width-8, 44), - uiMapping = new UIMapping(mappingTool, 4, 4, 140, 344), + uiDebugText = new UIDebugText(148, height-138, width-304, 44), + uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324), }; uiMapping.setVisible(false); logTime("Built overlay UI"); - // MIDI devices - midiListeners.add(new MidiListener().setEnabled(true)); - for (MidiInputDevice d : RWMidi.getInputDevices()) { - midiListeners.add(new MidiListener(d)); - } - SCMidiDevices.initializeStandardDevices(glucose); - logTime("Setup MIDI devices"); - // Setup camera midX = TRAILER_WIDTH/2.; midY = glucose.model.yMax/2; @@ -164,7 +170,7 @@ void setup() { println("Hit the 'p' key to toggle Panda Board output"); } -public class MidiListener { +public class MidiListener extends AbstractScrollItem { private boolean enabled = false; private final String name; @@ -173,44 +179,106 @@ public class MidiListener { name = d.getName(); } + class NoteMeta { + int channel; + int number; + NoteMeta(int channel, int number) { + this.channel = channel; + this.number = number; + } + } + + final Map keyToNote = new HashMap(); + MidiListener() { + name = "QWERTY Keyboard"; + mapNote('1', 0, 53); + mapNote('2', 1, 53); + mapNote('3', 2, 53); + mapNote('4', 3, 53); + mapNote('5', 4, 53); + mapNote('6', 5, 53); + mapNote('q', 0, 54); + mapNote('w', 1, 54); + mapNote('e', 2, 54); + mapNote('r', 3, 54); + mapNote('t', 4, 54); + mapNote('y', 5, 54); + mapNote('a', 0, 55); + mapNote('s', 1, 55); + mapNote('d', 2, 55); + mapNote('f', 3, 55); + mapNote('g', 4, 55); + mapNote('h', 5, 55); + mapNote('z', 0, 56); + mapNote('x', 1, 56); + mapNote('c', 2, 56); + mapNote('v', 3, 56); + mapNote('b', 4, 56); + mapNote('n', 5, 56); registerKeyEvent(this); - name = "Keyboard"; } - public String getName() { + void mapNote(char ch, int channel, int number) { + keyToNote.put(ch, new NoteMeta(channel, number)); + } + + public String getLabel() { return name; } public void keyEvent(KeyEvent e) { - if (e.getID() == KeyEvent.KEY_PRESSED) { - switch (e.getKeyChar()) { - case 'q': - noteOnReceived(new Note(60, 127)); + char c = Character.toLowerCase(e.getKeyChar()); + NoteMeta nm = keyToNote.get(c); + if (nm != null) { + switch (e.getID()) { + case KeyEvent.KEY_PRESSED: + noteOnReceived(new Note(Note.NOTE_ON, nm.channel, nm.number, 127)); break; - } - } else if (e.getID() == KeyEvent.KEY_RELEASED) { - switch (e.getKeyChar()) { - case 'q': - noteOffReceived(new Note(60, 0)); + case KeyEvent.KEY_RELEASED: + noteOffReceived(new Note(Note.NOTE_OFF, nm.channel, nm.number, 0)); break; } } } + public boolean isEnabled() { + return enabled; + } + + public boolean isSelected() { + return enabled; + } + + public void onMousePressed() { + setEnabled(!enabled); + } + public MidiListener setEnabled(boolean enabled) { if (enabled != this.enabled) { this.enabled = enabled; - // notify midi UI to update + uiMidi.redraw(); } return this; } + private SCPattern getFocusedPattern() { + return (SCPattern) uiMidi.getFocusedDeck().getActivePattern(); + } + + void programChangeReceived(ProgramChange pc) { + if (!enabled) { + return; + } + println("PC: " + pc.toString()); + } + void controllerChangeReceived(rwmidi.Controller cc) { if (!enabled) { return; } println("CC: " + cc.toString()); + getFocusedPattern().controllerChangeReceived(cc); } void noteOnReceived(Note note) { @@ -218,6 +286,8 @@ public class MidiListener { return; } println("Note On: " + note.toString()); + getFocusedPattern().noteOnReceived(note); + } void noteOffReceived(Note note) { @@ -225,6 +295,7 @@ public class MidiListener { return; } println("Note Off: " + note.toString()); + getFocusedPattern().noteOffReceived(note); } } @@ -253,7 +324,7 @@ void draw() { 0, -1, 0 ); - translate(0, 10, 0); + translate(0, 40, 0); noStroke(); fill(#141414); @@ -462,22 +533,26 @@ void keyPressed() { frameRate(++targetFramerate); break; case 'd': - debugMode = !debugMode; - println("Debug output: " + (debugMode ? "ON" : "OFF")); + if (!midiQwerty.isEnabled()) { + debugMode = !debugMode; + println("Debug output: " + (debugMode ? "ON" : "OFF")); + } break; case 'm': - mappingMode = !mappingMode; - uiPatternA.setVisible(!mappingMode); - uiMapping.setVisible(mappingMode); - if (mappingMode) { - restoreToPattern = lx.getPattern(); - lx.setPatterns(new LXPattern[] { mappingTool }); - } else { - lx.setPatterns(patterns); - LXTransition pop = restoreToPattern.getTransition(); - restoreToPattern.setTransition(null); - lx.goPattern(restoreToPattern); - restoreToPattern.setTransition(pop); + if (!midiQwerty.isEnabled()) { + mappingMode = !mappingMode; + uiPatternA.setVisible(!mappingMode); + uiMapping.setVisible(mappingMode); + if (mappingMode) { + restoreToPattern = lx.getPattern(); + lx.setPatterns(new LXPattern[] { mappingTool }); + } else { + lx.setPatterns(patterns); + LXTransition pop = restoreToPattern.getTransition(); + restoreToPattern.setTransition(null); + lx.goPattern(restoreToPattern); + restoreToPattern.setTransition(pop); + } } break; case 'p': diff --git a/_UIFramework.pde b/_UIFramework.pde index 3ca493b..aff72fe 100644 --- a/_UIFramework.pde +++ b/_UIFramework.pde @@ -751,8 +751,8 @@ public class UIScrollList extends UIObject { public void setScrollOffset(int offset) { scrollOffset = constrain(offset, 0, items.size() - numVisibleItems); - scrollYStart = (int) (scrollOffset * h / items.size()); - scrollYHeight = (int) (numVisibleItems * h / (float) items.size()); + scrollYStart = round(scrollOffset * h / items.size()); + scrollYHeight = round(numVisibleItems * h / items.size()); redraw(); } diff --git a/_UIImplementation.pde b/_UIImplementation.pde index c223444..226c68a 100644 --- a/_UIImplementation.pde +++ b/_UIImplementation.pde @@ -24,7 +24,7 @@ class UIPatternDeck extends UIWindow { for (LXPattern p : deck.getPatterns()) { items.add(new PatternScrollItem(p)); } - final UIScrollList patternList = new UIScrollList(1, yp, w-2, 160).setItems(items); + final UIScrollList patternList = new UIScrollList(1, yp, w-2, 140).setItems(items); patternList.addToContainer(this); yp += patternList.h + 10; @@ -86,57 +86,64 @@ class UIPatternDeck extends UIWindow { } } -class UICrossfader extends UIWindow { - - private final UIToggleSet displayMode; - - public UICrossfader(float x, float y, float w, float h) { - super("CROSSFADER", x, y, w, h); - +class UIBlendMode extends UIWindow { + public UIBlendMode(float x, float y, float w, float h) { + super("BLEND MODE", x, y, w, h); List items = new ArrayList(); for (LXTransition t : glucose.getTransitions()) { items.add(new TransitionScrollItem(t)); } final UIScrollList tList; - (tList = new UIScrollList(1, titleHeight, w-2, 120)).setItems(items).addToContainer(this); - new UIParameterSlider(4, titleHeight + 126, w-10, 24).setParameter(lx.engine.getDeck(1).getCrossfader()).addToContainer(this); - (displayMode = new UIToggleSet(4, 182, w-10, 20)).setOptions(new String[] { "A", "COMP", "B" }).setValue("COMP").addToContainer(this); - + (tList = new UIScrollList(1, titleHeight, w-2, 60)).setItems(items).addToContainer(this); + lx.engine.getDeck(1).addListener(new Engine.AbstractListener() { public void blendTransitionDidChange(Engine.Deck deck, LXTransition transition) { tList.redraw(); } }); } - - public String getDisplayMode() { - return displayMode.getValue(); + + class TransitionScrollItem extends AbstractScrollItem { + private final LXTransition transition; + private String label; + + TransitionScrollItem(LXTransition transition) { + this.transition = transition; + label = className(transition, "Transition"); + } + + public String getLabel() { + return label; + } + + public boolean isSelected() { + return transition == glucose.getSelectedTransition(); + } + + public boolean isPending() { + return false; + } + + public void onMousePressed() { + glucose.setSelectedTransition(transition); + } } + } -class TransitionScrollItem extends AbstractScrollItem { - private final LXTransition transition; - private String label; - - TransitionScrollItem(LXTransition transition) { - this.transition = transition; - label = className(transition, "Transition"); - } - - public String getLabel() { - return label; - } +class UICrossfader extends UIWindow { - public boolean isSelected() { - return transition == glucose.getSelectedTransition(); - } + private final UIToggleSet displayMode; - public boolean isPending() { - return false; + public UICrossfader(float x, float y, float w, float h) { + super("CROSSFADER", x, y, w, h); + + new UIParameterSlider(4, titleHeight, w-9, 32).setParameter(lx.engine.getDeck(1).getCrossfader()).addToContainer(this); + (displayMode = new UIToggleSet(4, titleHeight + 36, w-9, 20)).setOptions(new String[] { "A", "COMP", "B" }).setValue("COMP").addToContainer(this); } - public void onMousePressed() { - glucose.setSelectedTransition(transition); + public String getDisplayMode() { + return displayMode.getValue(); } } @@ -336,9 +343,8 @@ class UIMapping extends UIWindow { }).setRange(1, glucose.model.cubes.size()).addToContainer(this); yp += 24; - new UILabel(4, yp+8, w-10, 20).setLabel("COLORS").addToContainer(this); - yp += 24; - + yp += 10; + new UIScrollList(1, yp, w-2, 60).setItems(Arrays.asList(new ScrollItem[] { new ColorScrollItem(ColorScrollItem.COLOR_RED), new ColorScrollItem(ColorScrollItem.COLOR_GREEN), @@ -411,7 +417,7 @@ class UIMapping extends UIWindow { return false; } - public void select() { + public void onMousePressed() { switch (colorChannel) { case COLOR_RED: mappingTool.channelModeRed = !mappingTool.channelModeRed; break; case COLOR_GREEN: mappingTool.channelModeGreen = !mappingTool.channelModeGreen; break; @@ -451,6 +457,7 @@ class UIDebugText extends UIContext { pg.fill(#444444); pg.rect(0, 0, w, h); pg.textFont(defaultItemFont); + pg.textSize(10); pg.textAlign(LEFT, TOP); pg.fill(#cccccc); pg.text(line1, 4, 4); @@ -471,6 +478,26 @@ class UISpeed extends UIWindow { } } +class UIMidi extends UIWindow { + + final private UIToggleSet deckMode; + + UIMidi(List midiListeners, float x, float y, float w, float h) { + super("MIDI", x, y, w, h); + // Processing compiler doesn't seem to get that list of class objects also conform to interface + List scrollItems = new ArrayList(); + for (MidiListener ml : midiListeners) { + scrollItems.add(ml); + } + new UIScrollList(1, titleHeight, w-2, 80).setItems(scrollItems).addToContainer(this); + (deckMode = new UIToggleSet(4, 110, w-9, 20)).setOptions(new String[] { "A", "B" }).addToContainer(this); + } + + public Engine.Deck getFocusedDeck() { + return lx.engine.getDeck(deckMode.getValue() == "A" ? 0 : 1); + } +} + String className(Object p, String suffix) { String s = p.getClass().getName(); int li; diff --git a/code/GLucose.jar b/code/GLucose.jar index 86ea3c434bbda062133b610c5d4d7bc066111aa6..26b508fbce7d98f7413eefe22704297b0d74e543 100755 GIT binary patch delta 1352 zcmeA_&v@fJBTs-gGYc030|x^GL;pk`O{Twn6V3aXfb0{BAo8^eken>Yr~{(x8MQ%F z4x<)CP-^m@Adbnq7`Z^)V~lzr>Kmgz(2U9IOb>v(&790N93Z-#dkZs&w&mAi0nwSl zzZgMus^~W`9V^bn1mY)3dV%?CrDm~%_?}8GVDj zCh}|`@fB8!!Q#F)4PbTW9hDgQ82XK!dv7-uzh-7&P+*^|=q@t(y(8;p3FqHnH;KC6 z2bn$jo9AJW5xc$Wz(!j8FJ%U6z3lVNh{~Rg z{Kj{S{pP=%_wMh{@3+et9vdX>Xj)xZ@;HjeGiG{TSk#s$6Z{u6NOSf)Toz-}7cI0~ z)>-DU;T=c2w%sR+@68VgPfz{u z&GYuwX3e&eZJJi!r#W8iJaHjVxbdCkyz>SVY!#R0ukA?koMb(n>GCgw{IL5owEG@Z zTxhb*jVk^2z%xVh!`oMyHx{MuKFE0OX;ouHK|HtjjoXV3U6^b7r+;?Y(fVtrT3RyI zozJ~`C75~O)13p~W!!)K+Vyf>r?ST7j_cAgK@TfdJQ7q3`Lb}uB6dyDy&lCrt15al z`_#4OJ(8Td@{whUsQ!mZ3%9%8(^_V@I7v*k&$2H(sNv?6Z3pSL|Fk z+3tz;71i9tROg6KU1i>V^0D%j@ryQYyW6$kQ^ZQmXv+lOTn=|{$%Xe<%>H+PiuSz zwB>!X@s#7=E*|{A>6~&zqE5kX;gj--5+P*^`E+akefK_PFlVu8;|cjAmgoK-^H^bh z#cBIzo)Y~n7v5Si-+j0%&;F&Gs-v6Op##mwlXi4?m&ydC-(aw^ZgKN#cPrd!+VUTk zfq^-=e{)Kx9XS0j3DX9r|BKPqApT^&SW8f@^o(@?@w#ITLDarj6A-mJPG+(|oC1hx z6lV>h^5d)^5;h>}Wt<&|(u}tU8Ictq0OIY5cLY)Y;{!odP=Y^*TAUCJqJAWVfGEGj za1gaT(Gx^{N^}QN)=6F~G z|6&BusiNP&bgVcN6NsNE=>_Jmm72v4;(IE&fYtY_Ie_IaYW!jaiJ#ST2CEY?C7 zo5-_)#8+4?28;XJG=SBecT{5JE6p)>?!Dbu{F<48L4kd;qPxiC_l~TS15(*G%Q=4q zyGzvlKFIvZ-#ib4jM?o~2R7E)e3rlVYob9*gr%^QAFNx{mK$xtv68ELUFLC1KqqJ0<=hcZHi*>nY|(Gxt9*7hp=e zYqG+#Vzqo(dfB--H|HAv`}g~FI74^Dt)<*FsVDoysep-3>vxvw_bjd3jv8F<|H=Mp zSA?cqoLTPW=tPevZJR!?msrBZD0wXA@M?=`my>3k<_!qFRk}Dv^ufnj%gyhtdwq6# z)m6cb7%Cf(xP_WaPrZKwb3c6W2} ze{?uk{qL+tntIMYzr*8=WBX62`OTZqmG-|r>10ft^gaXCkG6O38d$aPJ^QpilOgB6 zr_5vByZ1uPU;lPu>!3DOF8f6FVlmj&66f|UBGA#5LnBQW#r0Rz_hY)jslFJW) zIf;u)f;tS__&p{o8rL|dcxsgh`g+D{ygb`qU&kTUX1v8s@JB1h@tgBc{MhqSaL?oA z2deW}-XGEYaPE6WdZ)PtgVN>~>T;&s^^CBr$H4$ha%)5Fz$y1_m^L`&O2t?+GL_{_ z4v)12CCo{&4j}4EtRaYEk23+~HQP8F5LF##4HDcDX9W?InJf^m01`2Zw*!e3#oL2; zo8tpO6l;Pbi1J7X1W~gS{6W;4gkTWmm>2@0rYDAjs1J#rAj&$)9YnPxd4Z@4N!}nz zHrWM4B`5oUsErWb_heTP&n^W_wWfgeolEfrYe)qPra-9GsbEunq=KE~mF9__%NC~D z%Y*W5lQXXmD