Few tiny cleanups
[SugarCubes.git] / _Internals.pde
index 4dd145a80d62964c190e6ff1bea08d84261c53a6..b593f3ef07b9c4aa7db088430348d3da9c653ae7 100644 (file)
@@ -4,7 +4,7 @@
  *         //\\   //\\                 //\\   //\\  
  *        ///\\\ ///\\\               ///\\\ ///\\\
  *        \\\/// \\\///               \\\/// \\\///
  *         //\\   //\\                 //\\   //\\  
  *        ///\\\ ///\\\               ///\\\ ///\\\
  *        \\\/// \\\///               \\\/// \\\///
- *         \\//   \\//                 \\//   \\//
+ *         \\//   \\//                 \\//   \\//H
  *
  *        EXPERTS ONLY!!              EXPERTS ONLY!!
  *
  *
  *        EXPERTS ONLY!!              EXPERTS ONLY!!
  *
  * for general animation work.
  */
 
  * for general animation work.
  */
 
-import glucose.*;
-import glucose.control.*;
-import glucose.effect.*;
-import glucose.model.*;
-import glucose.pattern.*;
-import glucose.transform.*;
-import glucose.transition.*;
 import heronarts.lx.*;
 import heronarts.lx.*;
-import heronarts.lx.control.*;
 import heronarts.lx.effect.*;
 import heronarts.lx.effect.*;
+import heronarts.lx.model.*;
 import heronarts.lx.modulator.*;
 import heronarts.lx.modulator.*;
+import heronarts.lx.parameter.*;
 import heronarts.lx.pattern.*;
 import heronarts.lx.pattern.*;
+import heronarts.lx.transform.*;
 import heronarts.lx.transition.*;
 import heronarts.lx.transition.*;
+import heronarts.lx.ui.*;
+import heronarts.lx.ui.component.*;
+import heronarts.lx.ui.control.*;
 import ddf.minim.*;
 import ddf.minim.analysis.*;
 import processing.opengl.*;
 import rwmidi.*;
 import ddf.minim.*;
 import ddf.minim.analysis.*;
 import processing.opengl.*;
 import rwmidi.*;
+import java.lang.reflect.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 
-final int VIEWPORT_WIDTH = 900;
-final int VIEWPORT_HEIGHT = 700;
+static final int VIEWPORT_WIDTH = 900;
+static final int VIEWPORT_HEIGHT = 700;
+
+static final int LEFT_DECK = 0;
+static final int RIGHT_DECK = 1;
 
 // The trailer is measured from the outside of the black metal (but not including the higher welded part on the front)
 
 // The trailer is measured from the outside of the black metal (but not including the higher welded part on the front)
-final float TRAILER_WIDTH = 240;
-final float TRAILER_DEPTH = 97;
-final float TRAILER_HEIGHT = 33;
+static final float TRAILER_WIDTH = 192;
+static final float TRAILER_DEPTH = 192;
+static final float TRAILER_HEIGHT = 33;
 
 int targetFramerate = 60;
 int startMillis, lastMillis;
 
 // Core engine variables
 
 int targetFramerate = 60;
 int startMillis, lastMillis;
 
 // Core engine variables
-GLucose glucose;
-HeronLX lx;
+LX lx;
+Model model;
 LXPattern[] patterns;
 LXPattern[] patterns;
+LXTransition[] transitions;
+Effects effects;
+LXEffect[] effectsArr;
+DiscreteParameter selectedEffect;
 MappingTool mappingTool;
 MappingTool mappingTool;
-PandaDriver[] pandaBoards;
+GrizzlyOutput[] grizzlies;
+PresetManager presetManager;
 MidiEngine midiEngine;
 
 // Display configuration mode
 boolean mappingMode = false;
 boolean debugMode = false;
 MidiEngine midiEngine;
 
 // Display configuration mode
 boolean mappingMode = false;
 boolean debugMode = false;
-DebugUI debugUI;
-boolean uiOn = true;
+boolean simulationOn = true;
+boolean diagnosticsOn = false;
 LXPattern restoreToPattern = null;
 PImage logo;
 LXPattern restoreToPattern = null;
 PImage logo;
+float[] hsb = new float[3];
 
 // Handles to UI objects
 
 // Handles to UI objects
-UIContext[] overlays;
 UIPatternDeck uiPatternA;
 UICrossfader uiCrossfader;
 UIMidi uiMidi;
 UIMapping uiMapping;
 UIDebugText uiDebugText;
 UIPatternDeck uiPatternA;
 UICrossfader uiCrossfader;
 UIMidi uiMidi;
 UIMapping uiMapping;
 UIDebugText uiDebugText;
-
-// Camera variables
-float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ;
+UISpeed uiSpeed;
 
 /**
  * Engine construction and initialization.
  */
 
 
 /**
  * Engine construction and initialization.
  */
 
-LXTransition _transition(GLucose glucose) {
-  return new DissolveTransition(glucose.lx).setDuration(1000);
+LXTransition _transition(LX lx) {
+  return new DissolveTransition(lx).setDuration(1000);
 }
 
 }
 
-LXPattern[] _leftPatterns(GLucose glucose) {
-  LXPattern[] patterns = patterns(glucose);
+LXPattern[] _leftPatterns(LX lx) {
+  LXPattern[] patterns = patterns(lx);
   for (LXPattern p : patterns) {
   for (LXPattern p : patterns) {
-    p.setTransition(_transition(glucose));
+    p.setTransition(_transition(lx));
   }
   return patterns;
 }
 
   }
   return patterns;
 }
 
-LXPattern[] _rightPatterns(GLucose glucose) {
-  LXPattern[] patterns = _leftPatterns(glucose);
+LXPattern[] _rightPatterns(LX lx) {
+  LXPattern[] patterns = _leftPatterns(lx);
   LXPattern[] rightPatterns = new LXPattern[patterns.length+1];
   int i = 0;
   LXPattern[] rightPatterns = new LXPattern[patterns.length+1];
   int i = 0;
-  rightPatterns[i++] = new BlankPattern(glucose).setTransition(_transition(glucose));
+  rightPatterns[i++] = new BlankPattern(lx).setTransition(_transition(lx));
   for (LXPattern p : patterns) {
     rightPatterns[i++] = p;
   }
   return rightPatterns;
 }
   for (LXPattern p : patterns) {
     rightPatterns[i++] = p;
   }
   return rightPatterns;
 }
-  
+
+LXEffect[] _effectsArray(Effects effects) {
+  List<LXEffect> effectList = new ArrayList<LXEffect>();
+  for (Field f : effects.getClass().getDeclaredFields()) {
+    try {
+      Object val = f.get(effects);
+      if (val instanceof LXEffect) {
+        effectList.add((LXEffect)val);
+      }
+    } catch (IllegalAccessException iax) {}
+  }
+  return effectList.toArray(new LXEffect[]{});
+}
+
+LXEffect getSelectedEffect() {
+  return effectsArr[selectedEffect.getValuei()];
+}
 
 void logTime(String evt) {
   int now = millis();
 
 void logTime(String evt) {
   int now = millis();
@@ -113,303 +137,279 @@ void setup() {
   // hint(ENABLE_OPENGL_4X_SMOOTH); // no discernable improvement?
   logTime("Created viewport");
 
   // hint(ENABLE_OPENGL_4X_SMOOTH); // no discernable improvement?
   logTime("Created viewport");
 
-  // Create the GLucose engine to run the cubes
-  glucose = new GLucose(this, buildModel());
-  lx = glucose.lx;
+  // Create the model
+  model = buildModel();
+  logTime("Built Model");
+  
+  // LX engine
+  lx = new LX(this, model);
   lx.enableKeyboardTempo();
   lx.enableKeyboardTempo();
-  logTime("Built GLucose engine");
+  logTime("Built LX engine");
+    
+  // Set the patterns
+  LXEngine engine = lx.engine;
+  engine.setPatterns(patterns = _leftPatterns(lx));
+  engine.addDeck(_rightPatterns(lx));
+  logTime("Built patterns");
+
+  // Transitions
+  transitions = transitions(lx);
+  lx.engine.getDeck(RIGHT_DECK).setFaderTransition(transitions[0]);
+  logTime("Built transitions");
   
   
+  // Effects
+  lx.addEffects(effectsArr = _effectsArray(effects = new Effects()));
+  selectedEffect = new DiscreteParameter("EFFECT", effectsArr.length);
+  logTime("Built effects");
+
+  // Preset manager
+  presetManager = new PresetManager();
+  logTime("Loaded presets");
+
   // MIDI devices
   midiEngine = new MidiEngine();
   logTime("Setup MIDI devices");
 
   // MIDI devices
   midiEngine = new MidiEngine();
   logTime("Setup MIDI devices");
 
-  // Set the patterns
-  Engine engine = lx.engine;
-  engine.setPatterns(patterns = _leftPatterns(glucose));
-  engine.addDeck(_rightPatterns(glucose));
-  logTime("Built patterns");
-  glucose.setTransitions(transitions(glucose));
-  logTime("Built transitions");
-  glucose.lx.addEffects(effects(glucose));
-  logTime("Built effects");
-    
   // Build output driver
   // Build output driver
-  PandaMapping[] pandaMappings = buildPandaList();
-  pandaBoards = new PandaDriver[pandaMappings.length];
-  int pbi = 0;
-  for (PandaMapping pm : pandaMappings) {
-    pandaBoards[pbi++] = new PandaDriver(pm.ip, glucose.model, pm);
+  grizzlies = new GrizzlyOutput[]{};
+  try {
+    grizzlies = buildGrizzlies();
+    for (LXOutput output : grizzlies) {
+      lx.addOutput(output);
+    }
+  } catch (Exception x) {
+    x.printStackTrace();
   }
   }
-  mappingTool = new MappingTool(glucose, pandaMappings);
-  logTime("Built PandaDriver");
+  logTime("Built Grizzly Outputs");
 
 
+  // Mapping tool
+  mappingTool = new MappingTool(lx);
+  logTime("Built Mapping Tool");
+  
   // Build overlay UI
   // Build overlay UI
-  debugUI = new DebugUI(pandaMappings);
-  overlays = new UIContext[] {
-    uiPatternA = new UIPatternDeck(lx.engine.getDeck(0), "PATTERN A", 4, 4, 140, 324),
+  UILayer[] layers = new UILayer[] {
+    // Camera layer
+    new UICameraLayer(lx.ui)
+      .setCenter(model.cx, model.cy, model.cz)
+      .setRadius(290).addComponent(new UICubesLayer()),
+    
+    // Left controls
+    uiPatternA = new UIPatternDeck(lx.ui, lx.engine.getDeck(LEFT_DECK), "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 UIBlendMode(4, 332, 140, 86),
     new UIEffects(4, 422, 140, 144),
     new UITempo(4, 570, 140, 50),
-    new UISpeed(4, 624, 140, 50),
+    uiSpeed = new UISpeed(4, 624, 140, 50),
         
         
-    new UIPatternDeck(lx.engine.getDeck(1), "PATTERN B", width-144, 4, 140, 324),
+    // Right controls
+    new UIPatternDeck(lx.ui, lx.engine.getDeck(RIGHT_DECK), "PATTERN B", width-144, 4, 140, 324),
     uiMidi = new UIMidi(midiEngine, width-144, 332, 140, 158),
     uiMidi = new UIMidi(midiEngine, width-144, 332, 140, 158),
-    new UIOutput(width-144, 494, 140, 106),
+    new UIOutput(grizzlies, width-144, 494, 140, 106),
     
     
+    // Crossfader
     uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86),
     
     uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86),
     
+    // Overlays
     uiDebugText = new UIDebugText(148, height-138, width-304, 44),
     uiDebugText = new UIDebugText(148, height-138, width-304, 44),
-    uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324),
+    uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324)
   };
   };
-  uiMapping.setVisible(false);
-  logTime("Built overlay UI");
+  uiMapping.setVisible(false);  
+  for (UILayer layer : layers) {
+    lx.ui.addLayer(layer);
+  }
+  logTime("Built UI");
 
   // Load logo image
   logo = loadImage("data/logo.png");
 
   // Load logo image
   logo = loadImage("data/logo.png");
-  
-  // Setup camera
-  midX = TRAILER_WIDTH/2.;
-  midY = glucose.model.yMax/2;
-  midZ = TRAILER_DEPTH/2.;
-  eyeR = -290;
-  eyeA = .15;
-  eyeY = midY + 70;
-  eyeX = midX + eyeR*sin(eyeA);
-  eyeZ = midZ + eyeR*cos(eyeA);
-  
-  // Add mouse scrolling event support
-  addMouseWheelListener(new java.awt.event.MouseWheelListener() { 
-    public void mouseWheelMoved(java.awt.event.MouseWheelEvent mwe) { 
-      mouseWheel(mwe.getWheelRotation());
-  }}); 
-  
+  logTime("Loaded logo image");
+      
   println("Total setup: " + (millis() - startMillis) + "ms");
   println("Total setup: " + (millis() - startMillis) + "ms");
-  println("Hit the 'p' key to toggle Panda Board output");
+  println("Hit the 'o' key to toggle live output");
 }
 
 }
 
+public SCPattern getPattern() {
+  return (SCPattern) lx.getPattern();
+}      
+
 /**
 /**
- * Core render loop and drawing functionality.
+ * Subclass of LXPattern specific to sugar cubes. These patterns
+ * get access to the state and geometry, and have some
+ * little helpers for interacting with the model.
  */
  */
-void draw() {
-  // Draws the simulation and the 2D UI overlay
-  background(40);
-  color[] colors = glucose.getColors();
-
-  String displayMode = uiCrossfader.getDisplayMode();
-  if (displayMode == "A") {
-    colors = lx.engine.getDeck(0).getColors();
-  } else if (displayMode == "B") {
-    colors = lx.engine.getDeck(1).getColors();
+public static abstract class SCPattern extends LXPattern {
+               
+  protected SCPattern(LX lx) {
+    super(lx);
   }
   }
-  if (debugMode) {
-    debugUI.maskColors(colors);
-  }
-
-  camera(
-    eyeX, eyeY, eyeZ,
-    midX, midY, midZ,
-    0, -1, 0
-  );
-
-  translate(0, 40, 0);
-
-  noStroke();
-  fill(#141414);
-  drawBox(0, -TRAILER_HEIGHT, 0, 0, 0, 0, TRAILER_WIDTH, TRAILER_HEIGHT, TRAILER_DEPTH, TRAILER_HEIGHT/2.);
-  fill(#070707);
-  stroke(#222222);
-  beginShape();
-  vertex(0, 0, 0);
-  vertex(TRAILER_WIDTH, 0, 0);
-  vertex(TRAILER_WIDTH, 0, TRAILER_DEPTH);
-  vertex(0, 0, TRAILER_DEPTH);
-  endShape();
-
-  // Draw the logo on the front of platform  
-  pushMatrix();
-  translate(0, 0, -1);
-  float s = .07;
-  scale(s, -s, s);
-  image(logo, TRAILER_WIDTH/2/s-logo.width/2, TRAILER_HEIGHT/2/s-logo.height/2-2/s);
-  popMatrix();
-  
-  noStroke();
-  if (glucose.model.bassBox.exists) {
-    drawBassBox(glucose.model.bassBox, false);
-  }
-  for (Speaker speaker : glucose.model.speakers) {
-    drawSpeaker(speaker);
+       
+  /**
+   * Reset this pattern to its default state.
+   */
+  public final void reset() {
+    for (LXParameter parameter : getParameters()) {
+      parameter.reset();
+    }
+    onReset();
   }
   }
-  for (Cube c : glucose.model.cubes) {
-    drawCube(c);
+       
+  /**
+   * Subclasses may override to add additional reset functionality.
+   */
+  protected /*abstract*/ void onReset() {}
+       
+  /**
+   * Invoked by the engine when a grid controller button press occurs
+   * 
+   * @param row Row index on the gird
+   * @param col Column index on the grid
+   * @return True if the event was consumed, false otherwise
+   */
+  public boolean gridPressed(int row, int col) {
+    return false;
   }
   }
-
-  noFill();
-  strokeWeight(2);
-  beginShape(POINTS);
-  for (Point p : glucose.model.points) {
-    stroke(colors[p.index]);
-    vertex(p.fx, p.fy, p.fz);
+       
+  /**
+   * Invoked by the engine when a grid controller button release occurs
+   * 
+   * @param row Row index on the gird
+   * @param col Column index on the grid
+   * @return True if the event was consumed, false otherwise
+   */
+  public boolean gridReleased(int row, int col) {
+    return false;
   }
   }
-  endShape();
-  
-  // 2D Overlay UI
-  drawUI();
-    
-  // Send output colors
-  color[] sendColors = glucose.getColors();
-  if (debugMode) {
-    debugUI.maskColors(colors);
+       
+  /**
+   * Invoked by engine when this pattern is focused an a midi note is received.  
+   * 
+   * @param note
+   * @return True if the pattern has consumed this note, false if the top-level
+   *         may handle it
+   */
+  public boolean noteOn(rwmidi.Note note) {
+    return false;
   }
   }
-  
-  // Gamma correction here. Apply a cubic to the brightness
-  // for better representation of dynamic range
-  for (int i = 0; i < colors.length; ++i) {
-    float b = brightness(colors[i]) / 100.f;
-    colors[i] = color(
-      hue(colors[i]),
-      saturation(colors[i]),
-      (b*b*b) * 100.
-    );
+       
+  /**
+   * Invoked by engine when this pattern is focused an a midi note off is received.  
+   * 
+   * @param note
+   * @return True if the pattern has consumed this note, false if the top-level
+   *         may handle it
+   */
+  public boolean noteOff(rwmidi.Note note) {
+    return false;
   }
   }
-  
-  // TODO(mcslee): move into GLucose engine
-  for (PandaDriver p : pandaBoards) {
-    p.send(colors);
+       
+  /**
+   * Invoked by engine when this pattern is focused an a controller is received  
+   * 
+   * @param note
+   * @return True if the pattern has consumed this controller, false if the top-level
+   *         may handle it
+   */
+  public boolean controllerChange(rwmidi.Controller controller) {
+    return false;
   }
 }
 
   }
 }
 
-void drawBassBox(BassBox b, boolean hasSub) {
+long simulationNanos = 0;
+
+/**
+ * Core render loop and drawing functionality.
+ */
+void draw() {
+  long drawStart = System.nanoTime();
   
   
-  float in = .15;
+  // Set background
+  background(40);
   
   
-  if (hasSub) {
-    noStroke();
-    fill(#191919);
-    pushMatrix();
-    translate(b.x + BassBox.EDGE_WIDTH/2., b.y + BassBox.EDGE_HEIGHT/2, b.z + BassBox.EDGE_DEPTH/2.);
-    box(BassBox.EDGE_WIDTH-20*in, BassBox.EDGE_HEIGHT-20*in, BassBox.EDGE_DEPTH-20*in);
-    popMatrix();
+  // Send colors
+  color[] sendColors = lx.getColors();  
+  long gammaStart = System.nanoTime();
+  // Gamma correction here. Apply a cubic to the brightness
+  // for better representation of dynamic range
+  for (int i = 0; i < sendColors.length; ++i) {
+    lx.RGBtoHSB(sendColors[i], hsb);
+    float b = hsb[2];
+    sendColors[i] = lx.hsb(360.*hsb[0], 100.*hsb[1], 100.*(b*b*b));
   }
   }
+  long gammaNanos = System.nanoTime() - gammaStart;
 
 
-  noStroke();
-  fill(#393939);
-  drawBox(b.x+in, b.y+in, b.z+in, 0, 0, 0, BassBox.EDGE_WIDTH-in*2, BassBox.EDGE_HEIGHT-in*2, BassBox.EDGE_DEPTH-in*2, Cube.CHANNEL_WIDTH-in);
-
-  pushMatrix();
-  translate(b.x+(Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT-in, b.z + BassBox.EDGE_DEPTH/2.);
-  float lastOffset = 0;
-  for (float offset : BoothFloor.STRIP_OFFSETS) {
-    translate(offset - lastOffset, 0, 0);
-    box(Cube.CHANNEL_WIDTH-in, 0, BassBox.EDGE_DEPTH - 2*in);
-    lastOffset = offset;
-  }
-  popMatrix();
-
-  pushMatrix();
-  translate(b.x + (Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT/2., b.z + in);
-  for (int j = 0; j < 2; ++j) {
-    pushMatrix();
-    for (int i = 0; i < BassBox.NUM_FRONT_STRUTS; ++i) {
-      translate(BassBox.FRONT_STRUT_SPACING, 0, 0);
-      box(Cube.CHANNEL_WIDTH-in, BassBox.EDGE_HEIGHT - in*2, 0);
-    }
-    popMatrix();
-    translate(0, 0, BassBox.EDGE_DEPTH - 2*in);
-  }
-  popMatrix();
-  
-  pushMatrix();
-  translate(b.x + in, b.y + BassBox.EDGE_HEIGHT/2., b.z + BassBox.SIDE_STRUT_SPACING + (Cube.CHANNEL_WIDTH-in)/2.);
-  box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
-  translate(BassBox.EDGE_WIDTH-2*in, 0, 0);
-  box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
-  popMatrix();
-  
-}
+  // Always draw FPS meter
+  drawFPS();
 
 
-void drawCube(Cube c) {
-  float in = .15;
-  noStroke();
-  fill(#393939);  
-  drawBox(c.x+in, c.y+in, c.z+in, c.rx, c.ry, c.rz, Cube.EDGE_WIDTH-in*2, Cube.EDGE_HEIGHT-in*2, Cube.EDGE_WIDTH-in*2, Cube.CHANNEL_WIDTH-in);
+  // TODO(mcslee): fix
+  long drawNanos = System.nanoTime() - drawStart;
+  long uiNanos = 0;
+
+  if (diagnosticsOn) {
+    drawDiagnostics(drawNanos, simulationNanos, uiNanos, gammaNanos);
+  }  
 }
 
 }
 
-void drawSpeaker(Speaker s) {
-  float in = .15;
-  
+void drawDiagnostics(long drawNanos, long simulationNanos, long uiNanos, long gammaNanos) {
+  float ws = 4 / 1000000.;
+  int thirtyfps = 1000000000 / 30;
+  int sixtyfps = 1000000000 / 60;
+  int x = width - 138;
+  int y = height - 14;
+  int h = 10;
+  noFill();
+  stroke(#999999);
+  rect(x, y, thirtyfps * ws, h);
   noStroke();
   noStroke();
-  fill(#191919);
-  pushMatrix();
-  translate(s.x, s.y, s.z);
-  rotate(s.ry / 180. * PI, 0, -1, 0);
-  translate(Speaker.EDGE_WIDTH/2., Speaker.EDGE_HEIGHT/2., Speaker.EDGE_DEPTH/2.);
-  box(Speaker.EDGE_WIDTH-20*in, Speaker.EDGE_HEIGHT-20*in, Speaker.EDGE_DEPTH-20*in);
-  translate(0, Speaker.EDGE_HEIGHT/2. + Speaker.EDGE_HEIGHT*.8/2, 0);
-
-  fill(#222222);
-  box(Speaker.EDGE_WIDTH*.6, Speaker.EDGE_HEIGHT*.8, Speaker.EDGE_DEPTH*.75);
-  popMatrix();
+  int xp = x;
+  float hv = 0;
+  for (long val : new long[] {lx.timer.drawNanos, simulationNanos, uiNanos, gammaNanos, lx.timer.outputNanos }) {
+    fill(lx.hsb(hv % 360, 100, 80));
+    rect(xp, y, val * ws, h-1);
+    hv += 140;
+    xp += val * ws;
+  }
+  noFill();
+  stroke(#333333);
+  line(x+sixtyfps*ws, y+1, x+sixtyfps*ws, y+h-1);
   
   
+  y = y - 14;
+  xp = x;
+  float tw = thirtyfps * ws;
+  noFill();
+  stroke(#999999);
+  rect(x, y, tw, h);
+  h = 5;
   noStroke();
   noStroke();
-  fill(#393939);  
-  drawBox(s.x+in, s.y+in, s.z+in, 0, s.ry, 0, Speaker.EDGE_WIDTH-in*2, Speaker.EDGE_HEIGHT-in*2, Speaker.EDGE_DEPTH-in*2, Cube.CHANNEL_WIDTH-in);
-}
-
-void drawBox(float x, float y, float z, float rx, float ry, float rz, float xd, float yd, float zd, float sw) {
-  pushMatrix();
-  translate(x, y, z);
-  rotate(rx / 180. * PI, -1, 0, 0);
-  rotate(ry / 180. * PI, 0, -1, 0);
-  rotate(rz / 180. * PI, 0, 0, -1);
-  for (int i = 0; i < 4; ++i) {
-    float wid = (i % 2 == 0) ? xd : zd;
-    
-    beginShape();
-    vertex(0, 0);
-    vertex(wid, 0);
-    vertex(wid, yd);
-    vertex(wid - sw, yd);
-    vertex(wid - sw, sw);
-    vertex(0, sw);
-    endShape();
-    beginShape();
-    vertex(0, sw);
-    vertex(0, yd);
-    vertex(wid - sw, yd);
-    vertex(wid - sw, yd - sw);
-    vertex(sw, yd - sw);
-    vertex(sw, sw);
-    endShape();
-
-    translate(wid, 0, 0);
-    rotate(HALF_PI, 0, -1, 0);
+  for (long val : new long[] {
+    lx.engine.timer.deckNanos,
+    lx.engine.timer.copyNanos,
+    lx.engine.timer.fxNanos}) {
+    float amt = val / (float) lx.timer.drawNanos;
+    fill(lx.hsb(hv % 360, 100, 80));
+    rect(xp, y, amt * tw, h-1);
+    hv += 140;
+    xp += amt * tw;
+  }
+  
+  xp = x;
+  y += h;
+  hv = 120;
+  for (long val : new long[] {
+    lx.engine.getDeck(0).timer.runNanos,
+    lx.engine.getDeck(1).timer.runNanos,
+    lx.engine.getDeck(1).getFaderTransition().timer.blendNanos}) {
+    float amt = val / (float) lx.timer.drawNanos;
+    fill(lx.hsb(hv % 360, 100, 80));
+    rect(xp, y, amt * tw, h-1);
+    hv += 140;
+    xp += amt * tw;
   }
   }
-  popMatrix();
 }
 
 }
 
-void drawUI() {
-  camera();
-  javax.media.opengl.GL gl = ((PGraphicsOpenGL)g).beginGL();
-  gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT);
-  ((PGraphicsOpenGL)g).endGL();
-  strokeWeight(1);
-
-  if (uiOn) {
-    for (UIContext context : overlays) {
-      context.draw();
-    }
-  }
-  
+void drawFPS() {  
   // Always draw FPS meter
   fill(#555555);
   textSize(9);
   textAlign(LEFT, BASELINE);
   text("FPS: " + ((int) (frameRate*10)) / 10. + " / " + targetFramerate + " (-/+)", 4, height-4);
   // Always draw FPS meter
   fill(#555555);
   textSize(9);
   textAlign(LEFT, BASELINE);
   text("FPS: " + ((int) (frameRate*10)) / 10. + " / " + targetFramerate + " (-/+)", 4, height-4);
-
-  if (debugMode) {
-    debugUI.draw();
-  }
 }
 
 
 }
 
 
@@ -421,6 +421,44 @@ void keyPressed() {
     mappingTool.keyPressed(uiMapping);
   }
   switch (key) {
     mappingTool.keyPressed(uiMapping);
   }
   switch (key) {
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+      if (!midiEngine.isQwertyEnabled()) {
+        presetManager.select(midiEngine.getFocusedDeck(), key - '1');
+      }
+      break;
+    
+    case '!':
+      if (!midiEngine.isQwertyEnabled()) presetManager.store(midiEngine.getFocusedDeck(), 0);
+      break;
+    case '@':
+      if (!midiEngine.isQwertyEnabled()) presetManager.store(midiEngine.getFocusedDeck(), 1);
+      break;
+    case '#':
+      if (!midiEngine.isQwertyEnabled()) presetManager.store(midiEngine.getFocusedDeck(), 2);
+      break;
+    case '$':
+      if (!midiEngine.isQwertyEnabled()) presetManager.store(midiEngine.getFocusedDeck(), 3);
+      break;
+    case '%':
+      if (!midiEngine.isQwertyEnabled()) presetManager.store(midiEngine.getFocusedDeck(), 4);
+      break;
+    case '^':
+      if (!midiEngine.isQwertyEnabled()) presetManager.store(midiEngine.getFocusedDeck(), 5);
+      break;
+    case '&':
+      if (!midiEngine.isQwertyEnabled()) presetManager.store(midiEngine.getFocusedDeck(), 6);
+      break;
+    case '*':
+      if (!midiEngine.isQwertyEnabled()) presetManager.store(midiEngine.getFocusedDeck(), 7);
+      break;
+      
     case '-':
     case '_':
       frameRate(--targetFramerate);
     case '-':
     case '_':
       frameRate(--targetFramerate);
@@ -430,7 +468,7 @@ void keyPressed() {
       frameRate(++targetFramerate);
       break; 
     case 'b':
       frameRate(++targetFramerate);
       break; 
     case 'b':
-      EFF_boom.trigger();
+      effects.boom.trigger();
       break;    
     case 'd':
       if (!midiEngine.isQwertyEnabled()) {
       break;    
     case 'd':
       if (!midiEngine.isQwertyEnabled()) {
@@ -455,69 +493,27 @@ void keyPressed() {
         }
       }
       break;
         }
       }
       break;
+    case 't':
+      if (!midiEngine.isQwertyEnabled()) {
+        lx.engine.setThreaded(!lx.engine.isThreaded());
+      }
+      break;
+    case 'o':
     case 'p':
     case 'p':
-      for (PandaDriver p : pandaBoards) {
-        p.toggle();
+      for (LXOutput output : grizzlies) {
+        output.enabled.toggle();
       }
       break;
       }
       break;
-    case 'u':
+    case 'q':
       if (!midiEngine.isQwertyEnabled()) {
       if (!midiEngine.isQwertyEnabled()) {
-        uiOn = !uiOn;
+        diagnosticsOn = !diagnosticsOn;
+      }
+      break;
+    case 's':
+      if (!midiEngine.isQwertyEnabled()) {
+        simulationOn = !simulationOn;
       }
       break;
   }
 }
 
       }
       break;
   }
 }
 
-/**
- * Top-level mouse event handling
- */
-int mx, my;
-void mousePressed() {
-  boolean debugged = false;
-  if (debugMode) {
-    debugged = debugUI.mousePressed();
-  }
-  if (!debugged) {
-    for (UIContext context : overlays) {
-      context.mousePressed(mouseX, mouseY);
-    }
-  }
-  mx = mouseX;
-  my = mouseY;
-}
-
-void mouseDragged() {
-  boolean dragged = false;
-  for (UIContext context : overlays) {
-    dragged |= context.mouseDragged(mouseX, mouseY);
-  }
-  if (!dragged) {
-    int dx = mouseX - mx;
-    int dy = mouseY - my;
-    mx = mouseX;
-    my = mouseY;
-    eyeA += dx*.003;
-    eyeX = midX + eyeR*sin(eyeA);
-    eyeZ = midZ + eyeR*cos(eyeA);
-    eyeY += dy;
-  }
-}
-
-void mouseReleased() {
-  for (UIContext context : overlays) {
-    context.mouseReleased(mouseX, mouseY);
-  }
-}
-
-void mouseWheel(int delta) {delta*=20;
-  boolean wheeled = false;
-  for (UIContext context : overlays) {
-    wheeled |= context.mouseWheel(mouseX, mouseY, delta);
-  }
-  
-  if (!wheeled) {
-    eyeR = constrain(eyeR - delta, -500, -80);
-    eyeX = midX + eyeR*sin(eyeA);
-    eyeZ = midZ + eyeR*cos(eyeA);
-  }
-}