rotate y finaly working for sinesphere
[SugarCubes.git] / _Internals.pde
index 3710e0eb7b8fafe6b19018e0c7db3291a4b86fe1..61f6c7d08d40009b22dbf8da487ed20c2ade8140 100644 (file)
@@ -30,6 +30,7 @@ import ddf.minim.*;
 import ddf.minim.analysis.*;
 import processing.opengl.*;
 import rwmidi.*;
 import ddf.minim.analysis.*;
 import processing.opengl.*;
 import rwmidi.*;
+import java.lang.reflect.*;
 
 final int VIEWPORT_WIDTH = 900;
 final int VIEWPORT_HEIGHT = 700;
 
 final int VIEWPORT_WIDTH = 900;
 final int VIEWPORT_HEIGHT = 700;
@@ -39,6 +40,9 @@ final float TRAILER_WIDTH = 240;
 final float TRAILER_DEPTH = 97;
 final float TRAILER_HEIGHT = 33;
 
 final float TRAILER_DEPTH = 97;
 final float TRAILER_HEIGHT = 33;
 
+final int MaxCubeHeight = 5;
+final int NumBackTowers = 11;
+
 int targetFramerate = 60;
 int startMillis, lastMillis;
 
 int targetFramerate = 60;
 int startMillis, lastMillis;
 
@@ -46,10 +50,11 @@ int startMillis, lastMillis;
 GLucose glucose;
 HeronLX lx;
 LXPattern[] patterns;
 GLucose glucose;
 HeronLX lx;
 LXPattern[] patterns;
+Effects effects;
 MappingTool mappingTool;
 PandaDriver[] pandaBoards;
 MappingTool mappingTool;
 PandaDriver[] pandaBoards;
-MidiListener midiQwertyKeys;
-MidiListener midiQwertyAPC;
+PresetManager presetManager;
+MidiEngine midiEngine;
 
 // Display configuration mode
 boolean mappingMode = false;
 
 // Display configuration mode
 boolean mappingMode = false;
@@ -57,6 +62,8 @@ boolean debugMode = false;
 DebugUI debugUI;
 boolean uiOn = true;
 LXPattern restoreToPattern = null;
 DebugUI debugUI;
 boolean uiOn = true;
 LXPattern restoreToPattern = null;
+PImage logo;
+float[] hsb = new float[3];
 
 // Handles to UI objects
 UIContext[] overlays;
 
 // Handles to UI objects
 UIContext[] overlays;
@@ -65,6 +72,7 @@ UICrossfader uiCrossfader;
 UIMidi uiMidi;
 UIMapping uiMapping;
 UIDebugText uiDebugText;
 UIMidi uiMidi;
 UIMapping uiMapping;
 UIDebugText uiDebugText;
+UISpeed uiSpeed;
 
 // Camera variables
 float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ;
 
 // Camera variables
 float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ;
@@ -72,14 +80,43 @@ float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ;
 /**
  * Engine construction and initialization.
  */
 /**
  * Engine construction and initialization.
  */
-LXPattern[] _patterns(GLucose glucose) {
+
+LXTransition _transition(GLucose glucose) {
+  return new DissolveTransition(glucose.lx).setDuration(1000);
+}
+
+LXPattern[] _leftPatterns(GLucose glucose) {
   LXPattern[] patterns = patterns(glucose);
   for (LXPattern p : patterns) {
   LXPattern[] patterns = patterns(glucose);
   for (LXPattern p : patterns) {
-    p.setTransition(new DissolveTransition(glucose.lx).setDuration(1000));
+    p.setTransition(_transition(glucose));
   }
   return patterns;
 }
 
   }
   return patterns;
 }
 
+LXPattern[] _rightPatterns(GLucose glucose) {
+  LXPattern[] patterns = _leftPatterns(glucose);
+  LXPattern[] rightPatterns = new LXPattern[patterns.length+1];
+  int i = 0;
+  rightPatterns[i++] = new BlankPattern(glucose).setTransition(_transition(glucose));
+  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[]{});
+} 
+
 void logTime(String evt) {
   int now = millis();
   println(evt + ": " + (now - lastMillis) + "ms");
 void logTime(String evt) {
   int now = millis();
   println(evt + ": " + (now - lastMillis) + "ms");
@@ -103,15 +140,23 @@ void setup() {
   logTime("Built GLucose engine");
   
   // Set the patterns
   logTime("Built GLucose engine");
   
   // Set the patterns
-  Engine engine = lx.engine;
-  engine.setPatterns(patterns = _patterns(glucose));
-  engine.addDeck(_patterns(glucose));
+  LXEngine engine = lx.engine;
+  engine.setPatterns(patterns = _leftPatterns(glucose));
+  engine.addDeck(_rightPatterns(glucose));
   logTime("Built patterns");
   glucose.setTransitions(transitions(glucose));
   logTime("Built transitions");
   logTime("Built patterns");
   glucose.setTransitions(transitions(glucose));
   logTime("Built transitions");
-  glucose.lx.addEffects(effects(glucose));
+  glucose.lx.addEffects(_effectsArray(effects = new Effects()));
   logTime("Built effects");
   logTime("Built effects");
-    
+
+  // Preset manager
+  presetManager = new PresetManager();
+  logTime("Loaded presets");
+
+  // MIDI devices
+  midiEngine = new MidiEngine();
+  logTime("Setup MIDI devices");
+
   // Build output driver
   PandaMapping[] pandaMappings = buildPandaList();
   pandaBoards = new PandaDriver[pandaMappings.length];
   // Build output driver
   PandaMapping[] pandaMappings = buildPandaList();
   pandaBoards = new PandaDriver[pandaMappings.length];
@@ -122,28 +167,17 @@ void setup() {
   mappingTool = new MappingTool(glucose, pandaMappings);
   logTime("Built PandaDriver");
 
   mappingTool = new MappingTool(glucose, pandaMappings);
   logTime("Built PandaDriver");
 
-  // MIDI devices
-  List<MidiListener> midiListeners = new ArrayList<MidiListener>();
-  midiListeners.add(midiQwertyKeys = new MidiListener(MidiListener.KEYS));
-  midiListeners.add(midiQwertyAPC = new MidiListener(MidiListener.APC));
-  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[] {
   // Build overlay UI
   debugUI = new DebugUI(pandaMappings);
   overlays = new UIContext[] {
-    uiPatternA = new UIPatternDeck(lx.engine.getDeck(0), "PATTERN A", 4, 4, 140, 324),
+    uiPatternA = new UIPatternDeck(lx.engine.getDeck(GLucose.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),
-    uiMidi = new UIMidi(midiListeners, width-144, 332, 140, 158),
+    new UIPatternDeck(lx.engine.getDeck(GLucose.RIGHT_DECK), "PATTERN B", width-144, 4, 140, 324),
+    uiMidi = new UIMidi(midiEngine, width-144, 332, 140, 158),
     new UIOutput(width-144, 494, 140, 106),
     
     uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86),
     new UIOutput(width-144, 494, 140, 106),
     
     uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86),
@@ -153,7 +187,10 @@ void setup() {
   };
   uiMapping.setVisible(false);
   logTime("Built overlay UI");
   };
   uiMapping.setVisible(false);
   logTime("Built overlay UI");
-    
+
+  // Load logo image
+  logo = loadImage("data/logo.png");
+  
   // Setup camera
   midX = TRAILER_WIDTH/2.;
   midY = glucose.model.yMax/2;
   // Setup camera
   midX = TRAILER_WIDTH/2.;
   midY = glucose.model.yMax/2;
@@ -163,6 +200,8 @@ void setup() {
   eyeY = midY + 70;
   eyeX = midX + eyeR*sin(eyeA);
   eyeZ = midZ + eyeR*cos(eyeA);
   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());
   addMouseWheelListener(new java.awt.event.MouseWheelListener() { 
     public void mouseWheelMoved(java.awt.event.MouseWheelEvent mwe) { 
       mouseWheel(mwe.getWheelRotation());
@@ -172,215 +211,25 @@ void setup() {
   println("Hit the 'p' key to toggle Panda Board output");
 }
 
   println("Hit the 'p' key to toggle Panda Board output");
 }
 
-public class MidiListener extends AbstractScrollItem {
-  
-  public static final int MIDI = 0;
-  public static final int KEYS = 1;
-  public static final int APC = 2;
-  
-  private boolean enabled = false;
-  private final String name;
-  
-  MidiListener(MidiInputDevice d) {
-    mode = MIDI;
-    d.createInput(this);
-    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>();
-  
-  private final int mode;
-  private int octaveShift = 0;
-  
-  MidiListener(int mode) {
-    this.mode = mode;
-    switch (mode) {
-      case APC:
-        name = "QWERTY (APC Mode)";
-        mapAPC();
-        break;
-      default:
-      case KEYS:
-        name = "QWERTY (Key Mode)";
-        mapKeys();
-        break;
-    }
-  }
-  
-  private void mapAPC() {
-    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);
-  }
-  
-  private void mapKeys() {
-    int note = 48;
-    mapNote('a', 1, note++);
-    mapNote('w', 1, note++);
-    mapNote('s', 1, note++);
-    mapNote('e', 1, note++);
-    mapNote('d', 1, note++);
-    mapNote('f', 1, note++);
-    mapNote('t', 1, note++);
-    mapNote('g', 1, note++);
-    mapNote('y', 1, note++);
-    mapNote('h', 1, note++);
-    mapNote('u', 1, note++);
-    mapNote('j', 1, note++);
-    mapNote('k', 1, note++);
-    mapNote('o', 1, note++);
-    mapNote('l', 1, note++);
-    registerKeyEvent(this);
-  }
-  
-  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 (!enabled) {
-      return;
-    }
-    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 + octaveShift*12, 127));
-          break;
-        case KeyEvent.KEY_RELEASED:
-          noteOffReceived(new Note(Note.NOTE_OFF, nm.channel, nm.number + octaveShift*12, 0));
-          break;
-      }
-    }
-    if ((mode == KEYS) && (e.getID() == KeyEvent.KEY_PRESSED)) {
-      switch (c) {
-        case 'z':
-          octaveShift = constrain(octaveShift-1, -4, 4);
-          break;
-        case 'x':
-          octaveShift = constrain(octaveShift+1, -4, 4);
-          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;
-      uiMidi.redraw();
-    }
-    return this;
-  }
-  
-  private SCPattern getFocusedPattern() {
-    return (SCPattern) uiMidi.getFocusedDeck().getActivePattern();
-  }
-  
-  void programChangeReceived(ProgramChange pc) {
-    if (!enabled) {
-      return;
-    }
-    if (uiMidi.logMidi()) {
-      println(getLabel() + " :: Program Change :: " + pc.getNumber());
-    }
-  }
-  
-  void controllerChangeReceived(rwmidi.Controller cc) {
-    if (!enabled) {
-      return;
-    }
-    if (uiMidi.logMidi()) {
-      println(getLabel() + " :: Controller :: " + cc.getCC() + ":" + cc.getValue());
-    }
-    getFocusedPattern().controllerChangeReceived(cc);
-  }
-
-  void noteOnReceived(Note note) {
-    if (!enabled) {
-      return;
-    }
-    if (uiMidi.logMidi()) {
-      println(getLabel() + " :: Note On  :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity());
-    }
-    getFocusedPattern().noteOnReceived(note);
-  }
-
-  void noteOffReceived(Note note) {
-    if (!enabled) {
-      return;
-    }
-    if (uiMidi.logMidi()) {
-      println(getLabel() + " :: Note Off :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity());
-    }
-    getFocusedPattern().noteOffReceived(note);
-  }
-
-}
-
 /**
  * Core render loop and drawing functionality.
  */
 void draw() {
   // Draws the simulation and the 2D UI overlay
   background(40);
 /**
  * Core render loop and drawing functionality.
  */
 void draw() {
   // Draws the simulation and the 2D UI overlay
   background(40);
-  color[] colors = glucose.getColors();
 
 
+  color[] simulationColors;
+  color[] sendColors;
+  simulationColors = sendColors = glucose.getColors();
   String displayMode = uiCrossfader.getDisplayMode();
   if (displayMode == "A") {
   String displayMode = uiCrossfader.getDisplayMode();
   if (displayMode == "A") {
-    colors = lx.engine.getDeck(0).getColors();
+    simulationColors = lx.engine.getDeck(GLucose.LEFT_DECK).getColors();
   } else if (displayMode == "B") {
   } else if (displayMode == "B") {
-    colors = lx.engine.getDeck(1).getColors();
+    simulationColors = lx.engine.getDeck(GLucose.RIGHT_DECK).getColors();
   }
   if (debugMode) {
   }
   if (debugMode) {
-    debugUI.maskColors(colors);
+    debugUI.maskColors(simulationColors);
+    debugUI.maskColors(sendColors);
   }
 
   camera(
   }
 
   camera(
@@ -402,12 +251,22 @@ void draw() {
   vertex(TRAILER_WIDTH, 0, TRAILER_DEPTH);
   vertex(0, 0, TRAILER_DEPTH);
   endShape();
   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();
   
   noStroke();
-//  drawBassBox(glucose.model.bassBox);
-//  for (Speaker s : glucose.model.speakers) {
-//    drawSpeaker(s);
-//  }
+  if (glucose.model.bassBox.exists) {
+    drawBassBox(glucose.model.bassBox, false);
+  }
+  for (Speaker speaker : glucose.model.speakers) {
+    drawSpeaker(speaker);
+  }
   for (Cube c : glucose.model.cubes) {
     drawCube(c);
   }
   for (Cube c : glucose.model.cubes) {
     drawCube(c);
   }
@@ -415,51 +274,41 @@ void draw() {
   noFill();
   strokeWeight(2);
   beginShape(POINTS);
   noFill();
   strokeWeight(2);
   beginShape(POINTS);
-  // TODO(mcslee): restore when bassBox/speakers are right again
-  // for (Point p : glucose.model.points) {
-  for (Cube cube : glucose.model.cubes) {
-    for (Point p : cube.points) {
-      stroke(colors[p.index]);
-      vertex(p.fx, p.fy, p.fz);
-    }
+  for (Point p : glucose.model.points) {
+    stroke(simulationColors[p.index]);
+    vertex(p.x, p.y, p.z);
   }
   endShape();
   
   // 2D Overlay UI
   drawUI();
     
   }
   endShape();
   
   // 2D Overlay UI
   drawUI();
     
-  // Send output colors
-  color[] sendColors = glucose.getColors();
-  if (debugMode) {
-    debugUI.maskColors(colors);
-  }
-  
   // Gamma correction here. Apply a cubic to the brightness
   // for better representation of dynamic range
   // 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.
-    );
+  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));
   }
   
   // TODO(mcslee): move into GLucose engine
   for (PandaDriver p : pandaBoards) {
   }
   
   // TODO(mcslee): move into GLucose engine
   for (PandaDriver p : pandaBoards) {
-    p.send(colors);
+    p.send(sendColors);
   }
 }
 
   }
 }
 
-void drawBassBox(BassBox b) {
+void drawBassBox(BassBox b, boolean hasSub) {
+  
   float in = .15;
   float in = .15;
-
-  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();
+  
+  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();
+  }
 
   noStroke();
   fill(#393939);
 
   noStroke();
   fill(#393939);
@@ -581,6 +430,7 @@ void drawUI() {
   }
 }
 
   }
 }
 
+
 /**
  * Top-level keyboard event handling
  */
 /**
  * Top-level keyboard event handling
  */
@@ -589,6 +439,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);
@@ -596,15 +484,18 @@ void keyPressed() {
     case '=':
     case '+':
       frameRate(++targetFramerate);
     case '=':
     case '+':
       frameRate(++targetFramerate);
-      break;
+      break; 
+    case 'b':
+      effects.boom.trigger();
+      break;    
     case 'd':
     case 'd':
-      if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
+      if (!midiEngine.isQwertyEnabled()) {
         debugMode = !debugMode;
         println("Debug output: " + (debugMode ? "ON" : "OFF"));
       }
       break;
     case 'm':
         debugMode = !debugMode;
         println("Debug output: " + (debugMode ? "ON" : "OFF"));
       }
       break;
     case 'm':
-      if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
+      if (!midiEngine.isQwertyEnabled()) {
         mappingMode = !mappingMode;
         uiPatternA.setVisible(!mappingMode);
         uiMapping.setVisible(mappingMode);
         mappingMode = !mappingMode;
         uiPatternA.setVisible(!mappingMode);
         uiMapping.setVisible(mappingMode);
@@ -620,13 +511,18 @@ void keyPressed() {
         }
       }
       break;
         }
       }
       break;
+    case 't':
+      if (!midiEngine.isQwertyEnabled()) {
+        lx.engine.setThreaded(!lx.engine.isThreaded());
+      }
+      break;
     case 'p':
       for (PandaDriver p : pandaBoards) {
         p.toggle();
       }
       break;
     case 'u':
     case 'p':
       for (PandaDriver p : pandaBoards) {
         p.toggle();
       }
       break;
     case 'u':
-      if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
+      if (!midiEngine.isQwertyEnabled()) {
         uiOn = !uiOn;
       }
       break;
         uiOn = !uiOn;
       }
       break;
@@ -673,7 +569,7 @@ void mouseReleased() {
     context.mouseReleased(mouseX, mouseY);
   }
 }
     context.mouseReleased(mouseX, mouseY);
   }
 }
+
 void mouseWheel(int delta) {
   boolean wheeled = false;
   for (UIContext context : overlays) {
 void mouseWheel(int delta) {
   boolean wheeled = false;
   for (UIContext context : overlays) {