Add MIDI inputs to UI, with focused pattern deck to route input
authorMark Slee <mcslee@Mark-Slees-MacBook-Pro.local>
Fri, 20 Sep 2013 01:16:15 +0000 (18:16 -0700)
committerMark Slee <mcslee@Mark-Slees-MacBook-Pro.local>
Fri, 20 Sep 2013 01:16:15 +0000 (18:16 -0700)
DanHorwitz.pde
DanUtil.pde
MarkSlee.pde
_Internals.pde
_UIFramework.pde
_UIImplementation.pde
code/GLucose.jar

index 3b7e1d6ef37074bc5f59e537052e8c51b1021c5c..b15bad9d1b6a801a8fd6ebe84610043f3248f8c0 100755 (executable)
@@ -17,7 +17,7 @@ public class Pong extends DPat {
            pChoose = addPick   ("Animiation"   , 0     , 3     );\r
        }\r
 \r
-       void    StartRun(int deltaMs)   { cRad = xdMax*pSize.Val()/6; }\r
+       void    StartRun(double deltaMs)        { cRad = xdMax*pSize.Val()/6; }\r
        color   CalcPoint(xyz p)                {\r
                xyz v = new xyz(x.getValuef(), y.getValuef(), z.getValuef());\r
                switch(pChoose.Cur()) {\r
@@ -56,7 +56,7 @@ public class Noise extends DPat
                pSymm   = addPick("Symmetry", 0, 4);    pChoose         = addPick("Animation", 1, 6);\r
        }\r
 \r
-       void StartRun(int deltaMs) {\r
+       void StartRun(double deltaMs) {\r
                zTime   += deltaMs*(pSpeed.Val()-.5)*.002       ;\r
                zTheta  += deltaMs*(pRotZ .Val()-.5)*.01        ;\r
                rtime   += deltaMs;\r
@@ -135,7 +135,7 @@ public class Play extends DPat
        float   LastBeat=3, LastMeasure=3;\r
        int             CurRandTempo = 1;\r
 \r
-       void StartRun(int deltaMs) {\r
+       void StartRun(double deltaMs) {\r
                t = lx.tempo.rampf();\r
                a = pAmp.Val();\r
 \r
index b7ad56a798c31b240e700ea8b4c62fd19fa20e47..7bcd56a2bfda9e15050d72db8baec6d33d45016c 100644 (file)
@@ -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();
                }
index 9804b5cf705f026040b663f6e4211ae0fa36d559..5d08a8600c805c7d5aeee90333a77a62769cf144 100644 (file)
@@ -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);
index d4a5fcc76159767c41cf2a88aeffb3ff5e109c69..e57685ddef0dbbc0b12fdc664ee2ccf46655754e 100644 (file)
@@ -48,7 +48,7 @@ HeronLX lx;
 LXPattern[] patterns;
 MappingTool mappingTool;
 PandaDriver[] pandaBoards;
-final List<MidiListener> midiListeners = new ArrayList<MidiListener>();
+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<MidiListener> midiListeners = new ArrayList<MidiListener>();
+  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<Character, NoteMeta> keyToNote = new HashMap<Character, NoteMeta>();
+  
   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':
index 3ca493b1658d365258104bc62381f826a35d8042..aff72fe46a891d93df317c8478ab3976e6598d5d 100644 (file)
@@ -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();
   }
   
index c223444ef2eb81f0831a96e269cbd2bbea76224c..226c68a124655f5312c8d2fdcb58734e25086d96 100644 (file)
@@ -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<ScrollItem> items = new ArrayList<ScrollItem>();
     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<MidiListener> 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<ScrollItem> scrollItems = new ArrayList<ScrollItem>();
+    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;
index 86ea3c434bbda062133b610c5d4d7bc066111aa6..26b508fbce7d98f7413eefe22704297b0d74e543 100755 (executable)
Binary files a/code/GLucose.jar and b/code/GLucose.jar differ