new mappings for Friday sep 20 video shoot
[SugarCubes.git] / _Internals.pde
index 5c3f7cd5907431266f2d545637eaf83f0b617b5e..f872d9b7a733086578108e39160096e6da27b36d 100644 (file)
@@ -40,28 +40,39 @@ final float TRAILER_DEPTH = 97;
 final float TRAILER_HEIGHT = 33;
 
 int targetFramerate = 60;
-
 int startMillis, lastMillis;
+
+// Core engine variables
 GLucose glucose;
 HeronLX lx;
-MappingTool mappingTool;
 LXPattern[] patterns;
-LXTransition[] transitions;
-LXEffect[] effects;
+MappingTool mappingTool;
 PandaDriver[] pandaBoards;
+MidiListener midiQwertyKeys;
+MidiListener midiQwertyAPC;
+
+// Display configuration mode
 boolean mappingMode = false;
 boolean debugMode = false;
 DebugUI debugUI;
-String displayMode;
+boolean uiOn = true;
+LXPattern restoreToPattern = null;
+PImage logo;
 
+// Handles to UI objects
 UIContext[] overlays;
 UIPatternDeck uiPatternA;
+UICrossfader uiCrossfader;
+UIMidi uiMidi;
 UIMapping uiMapping;
 UIDebugText uiDebugText;
 
 // Camera variables
 float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ;
 
+/**
+ * Engine construction and initialization.
+ */
 LXPattern[] _patterns(GLucose glucose) {
   LXPattern[] patterns = patterns(glucose);
   for (LXPattern p : patterns) {
@@ -70,6 +81,12 @@ LXPattern[] _patterns(GLucose glucose) {
   return patterns;
 }
 
+void logTime(String evt) {
+  int now = millis();
+  println(evt + ": " + (now - lastMillis) + "ms");
+  lastMillis = now;
+}
+
 void setup() {
   startMillis = lastMillis = millis();
 
@@ -88,13 +105,12 @@ void setup() {
   
   // Set the patterns
   Engine engine = lx.engine;
-  glucose.setTransitions(transitions = transitions(glucose));
-  logTime("Built transitions");
   engine.setPatterns(patterns = _patterns(glucose));
   engine.addDeck(_patterns(glucose));
-  engine.getDeck(1).setBlendTransition(transitions[0]);
   logTime("Built patterns");
-  glucose.lx.addEffects(effects = effects(glucose));
+  glucose.setTransitions(transitions(glucose));
+  logTime("Built transitions");
+  glucose.lx.addEffects(effects(glucose));
   logTime("Built effects");
     
   // Build output driver
@@ -106,31 +122,42 @@ void setup() {
   }
   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[] {
-    uiPatternA = new UIPatternDeck(lx.engine.getDeck(0), "PATTERN A", 4, 4, 140, 344),
-    new UICrossfader(4, 352, 140, 212),
+    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, 158),
+    new UIOutput(width-144, 494, 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 UIOutput(width-144, 552, 140, 122),
+    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
-  for (MidiInputDevice d : RWMidi.getInputDevices()) {
-    d.createInput(this);
-  }
-  SCMidiDevices.initializeStandardDevices(glucose);
-  logTime("Setup MIDI devices");
-    
+
+  // Load logo image
+  logo = loadImage("data/logo.png");
+  
   // Setup camera
   midX = TRAILER_WIDTH/2.;
   midY = glucose.model.yMax/2;
@@ -149,35 +176,208 @@ void setup() {
   println("Hit the 'p' key to toggle Panda Board output");
 }
 
-
-void controllerChangeReceived(rwmidi.Controller cc) {
-  if (debugMode) {
-    println("CC: " + cc.toString());
+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 (debugMode) {
-    println("Note On: " + note.toString());
+  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 (debugMode) {
-    println("Note Off: " + note.toString());
+  void noteOffReceived(Note note) {
+    if (!enabled) {
+      return;
+    }
+    if (uiMidi.logMidi()) {
+      println(getLabel() + " :: Note Off :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity());
+    }
+    getFocusedPattern().noteOffReceived(note);
   }
-}
 
-void logTime(String evt) {
-  int now = millis();
-  println(evt + ": " + (now - lastMillis) + "ms");
-  lastMillis = now;
 }
 
+/**
+ * Core render loop and drawing functionality.
+ */
 void draw() {
   // Draws the simulation and the 2D UI overlay
   background(40);
-  color[] colors = glucose.getColors();;
+  color[] colors = glucose.getColors();
+
+  String displayMode = uiCrossfader.getDisplayMode();
   if (displayMode == "A") {
     colors = lx.engine.getDeck(0).getColors();
   } else if (displayMode == "B") {
@@ -193,7 +393,7 @@ void draw() {
     0, -1, 0
   );
 
-  translate(0, 10, 0);
+  translate(0, 40, 0);
 
   noStroke();
   fill(#141414);
@@ -206,6 +406,14 @@ void draw() {
   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();
 //  drawBassBox(glucose.model.bassBox);
@@ -385,9 +593,10 @@ void drawUI() {
   }
 }
 
-boolean uiOn = true;
-LXPattern restoreToPattern = null;
 
+/**
+ * Top-level keyboard event handling
+ */
 void keyPressed() {
   if (mappingMode) {
     mappingTool.keyPressed(uiMapping);
@@ -400,21 +609,28 @@ void keyPressed() {
     case '=':
     case '+':
       frameRate(++targetFramerate);
-      break;
+      break;      
     case 'd':
-      debugMode = !debugMode;
-      println("Debug output: " + (debugMode ? "ON" : "OFF"));
+      if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.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);
-        lx.goPattern(restoreToPattern);
+      if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.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':
@@ -423,11 +639,16 @@ void keyPressed() {
       }
       break;
     case 'u':
-      uiOn = !uiOn;
+      if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
+        uiOn = !uiOn;
+      }
       break;
   }
 }
 
+/**
+ * Top-level mouse event handling
+ */
 int mx, my;
 void mousePressed() {
   boolean debugged = false;
@@ -464,8 +685,6 @@ void mouseReleased() {
   for (UIContext context : overlays) {
     context.mouseReleased(mouseX, mouseY);
   }
-
-  // ui.mouseReleased();
 }
  
 void mouseWheel(int delta) {