X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=_Internals.pde;h=f872d9b7a733086578108e39160096e6da27b36d;hb=52c42820180ce238f4af45972faab6dce693b3a8;hp=5035ed59b6f8aeef7f4c7f232466929222924211;hpb=922def98b6cc88f3aa8784b8968d8a5aa1bfc1a2;p=SugarCubes.git diff --git a/_Internals.pde b/_Internals.pde index 5035ed5..f872d9b 100644 --- a/_Internals.pde +++ b/_Internals.pde @@ -40,25 +40,53 @@ 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; -OverlayUI ui; -ControlUI controlUI; -MappingUI mappingUI; +MappingTool mappingTool; PandaDriver[] pandaBoards; +MidiListener midiQwertyKeys; +MidiListener midiQwertyAPC; + +// Display configuration mode boolean mappingMode = false; boolean debugMode = false; DebugUI debugUI; +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) { + p.setTransition(new DissolveTransition(glucose.lx).setDuration(1000)); + } + return patterns; +} + +void logTime(String evt) { + int now = millis(); + println(evt + ": " + (now - lastMillis) + "ms"); + lastMillis = now; +} + void setup() { startMillis = lastMillis = millis(); @@ -76,12 +104,14 @@ void setup() { logTime("Built GLucose engine"); // Set the patterns - glucose.lx.setPatterns(patterns = patterns(glucose)); + Engine engine = lx.engine; + engine.setPatterns(patterns = _patterns(glucose)); + engine.addDeck(_patterns(glucose)); logTime("Built patterns"); - glucose.lx.addEffects(effects = effects(glucose)); - logTime("Built effects"); - glucose.setTransitions(transitions = transitions(glucose)); + glucose.setTransitions(transitions(glucose)); logTime("Built transitions"); + glucose.lx.addEffects(effects(glucose)); + logTime("Built effects"); // Build output driver PandaMapping[] pandaMappings = buildPandaList(); @@ -92,27 +122,49 @@ void setup() { } mappingTool = new MappingTool(glucose, pandaMappings); logTime("Built PandaDriver"); - - // Build overlay UI - ui = controlUI = new ControlUI(); - mappingUI = new MappingUI(mappingTool); - debugUI = new DebugUI(pandaMappings); - logTime("Built overlay UI"); - + // MIDI devices - for (MidiInputDevice d : RWMidi.getInputDevices()) { - d.createInput(this); + List midiListeners = new ArrayList(); + 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, 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), + uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86), + + uiDebugText = new UIDebugText(148, height-138, width-304, 44), + uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324), + }; + uiMapping.setVisible(false); + logTime("Built overlay UI"); + + // Load logo image + logo = loadImage("data/logo.png"); + // Setup camera - midX = TRAILER_WIDTH/2. + 20; + midX = TRAILER_WIDTH/2.; midY = glucose.model.yMax/2; midZ = TRAILER_DEPTH/2.; eyeR = -290; eyeA = .15; - eyeY = midY + 20; + eyeY = midY + 70; eyeX = midX + eyeR*sin(eyeA); eyeZ = midZ + eyeR*cos(eyeA); addMouseWheelListener(new java.awt.event.MouseWheelListener() { @@ -124,35 +176,213 @@ 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 keyToNote = new HashMap(); + + 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(); + + String displayMode = uiCrossfader.getDisplayMode(); + if (displayMode == "A") { + colors = lx.engine.getDeck(0).getColors(); + } else if (displayMode == "B") { + colors = lx.engine.getDeck(1).getColors(); + } if (debugMode) { debugUI.maskColors(colors); } @@ -163,6 +393,8 @@ void draw() { 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.); @@ -174,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); @@ -197,16 +437,13 @@ void draw() { } endShape(); - // 2D Overlay - camera(); - javax.media.opengl.GL gl = ((PGraphicsOpenGL)g).beginGL(); - gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT); - ((PGraphicsOpenGL)g).endGL(); - strokeWeight(1); + // 2D Overlay UI drawUI(); - + + // Send output colors + color[] sendColors = glucose.getColors(); if (debugMode) { - debugUI.draw(); + debugUI.maskColors(colors); } // Gamma correction here. Apply a cubic to the brightness @@ -333,28 +570,38 @@ void drawBox(float x, float y, float z, float rx, float ry, float rz, float xd, } 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) { - ui.draw(); - } else { - ui.drawHelpTip(); + for (UIContext context : overlays) { + context.draw(); + } } - ui.drawFPS(); - ui.drawDanText(); -} + + // Always draw FPS meter + fill(#555555); + textSize(9); + textAlign(LEFT, BASELINE); + text("FPS: " + ((int) (frameRate*10)) / 10. + " / " + targetFramerate + " (-/+)", 4, height-4); -boolean uiOn = true; -int restoreToIndex = -1; + if (debugMode) { + debugUI.draw(); + } +} -boolean doDual = false; +/** + * Top-level keyboard event handling + */ void keyPressed() { if (mappingMode) { - mappingTool.keyPressed(); + mappingTool.keyPressed(uiMapping); } switch (key) { - case 'w': - doDual = !doDual; - break; case '-': case '_': frameRate(--targetFramerate); @@ -362,27 +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; - if (mappingMode) { - LXPattern pattern = lx.getPattern(); - for (int i = 0; i < patterns.length; ++i) { - if (pattern == patterns[i]) { - restoreToIndex = i; - break; - } + 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); } - ui = mappingUI; - lx.setPatterns(new LXPattern[] { mappingTool }); - } else { - ui = controlUI; - lx.setPatterns(patterns); - lx.goIndex(restoreToIndex); } break; case 'p': @@ -391,27 +639,37 @@ 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() { - ui.mousePressed(); - if (mouseX < ui.leftPos) { - if (debugMode) { - debugUI.mousePressed(); - } - mx = mouseX; - my = mouseY; + boolean debugged = false; + if (debugMode) { + debugged = debugUI.mousePressed(); + } + if (!debugged) { + for (UIContext context : overlays) { + context.mousePressed(mouseX, mouseY); + } } + mx = mouseX; + my = mouseY; } void mouseDragged() { - if (mouseX > ui.leftPos) { - ui.mouseDragged(); - } else { + boolean dragged = false; + for (UIContext context : overlays) { + dragged |= context.mouseDragged(mouseX, mouseY); + } + if (!dragged) { int dx = mouseX - mx; int dy = mouseY - my; mx = mouseX; @@ -424,13 +682,18 @@ void mouseDragged() { } void mouseReleased() { - ui.mouseReleased(); + for (UIContext context : overlays) { + context.mouseReleased(mouseX, mouseY); + } } void mouseWheel(int delta) { - if (mouseX > ui.leftPos) { - ui.mouseWheel(delta); - } else { + 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);