X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=_Internals.pde;h=425ea0e3c8a34532ef54f39f10bca368be15ba28;hb=270a8b44cd8296bb40fa74e4fcf2e7f7c4f1192c;hp=b6d835ceb923b9c00becba042121ed217526b8ed;hpb=30bf6510d87dbce8e36b03564807c1a5f4718f9d;p=SugarCubes.git diff --git a/_Internals.pde b/_Internals.pde index b6d835c..425ea0e 100644 --- a/_Internals.pde +++ b/_Internals.pde @@ -4,7 +4,7 @@ * //\\ //\\ //\\ //\\ * ///\\\ ///\\\ ///\\\ ///\\\ * \\\/// \\\/// \\\/// \\\/// - * \\// \\// \\// \\// + * \\// \\// \\// \\//H * * EXPERTS ONLY!! EXPERTS ONLY!! * @@ -14,22 +14,19 @@ */ 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.control.*; import heronarts.lx.effect.*; import heronarts.lx.modulator.*; +import heronarts.lx.parameter.*; import heronarts.lx.pattern.*; +import heronarts.lx.transform.*; import heronarts.lx.transition.*; import ddf.minim.*; import ddf.minim.analysis.*; import processing.opengl.*; import rwmidi.*; +import java.lang.reflect.*; final int VIEWPORT_WIDTH = 900; final int VIEWPORT_HEIGHT = 700; @@ -44,20 +41,24 @@ int startMillis, lastMillis; // Core engine variables GLucose glucose; -HeronLX lx; +LX lx; LXPattern[] patterns; +Effects effects; MappingTool mappingTool; PandaDriver[] pandaBoards; -SCMidiInput midiQwertyKeys; -SCMidiInput midiQwertyAPC; +PresetManager presetManager; +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; +float[] hsb = new float[3]; // Handles to UI objects UIContext[] overlays; @@ -66,6 +67,7 @@ UICrossfader uiCrossfader; UIMidi uiMidi; UIMapping uiMapping; UIDebugText uiDebugText; +UISpeed uiSpeed; // Camera variables float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ; @@ -73,14 +75,43 @@ float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ; /** * 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) { - p.setTransition(new DissolveTransition(glucose.lx).setDuration(1000)); + p.setTransition(_transition(glucose)); } 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 effectList = new ArrayList(); + 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"); @@ -102,17 +133,25 @@ void setup() { lx = glucose.lx; lx.enableKeyboardTempo(); 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"); - glucose.lx.addEffects(effects(glucose)); + glucose.lx.addEffects(_effectsArray(effects = new 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]; @@ -123,28 +162,17 @@ void setup() { mappingTool = new MappingTool(glucose, pandaMappings); logTime("Built PandaDriver"); - // MIDI devices - List midiControllers = new ArrayList(); - midiControllers.add(midiQwertyKeys = new SCMidiInput(SCMidiInput.KEYS)); - midiControllers.add(midiQwertyAPC = new SCMidiInput(SCMidiInput.APC)); - for (MidiInputDevice device : RWMidi.getInputDevices()) { - boolean enableDevice = device.getName().contains("APC"); - midiControllers.add(new SCMidiInput(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), + 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 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(midiControllers, 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), @@ -167,6 +195,8 @@ void setup() { 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()); @@ -176,239 +206,121 @@ void setup() { println("Hit the 'p' key to toggle Panda Board output"); } -public interface SCMidiInputListener { - public void onEnabled(SCMidiInput controller, boolean enabled); -} - -public class SCMidiInput 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; - private final int mode; - private int octaveShift = 0; - - class NoteMeta { - int channel; - int number; - NoteMeta(int channel, int number) { - this.channel = channel; - this.number = number; - } - } - - final Map keyToNote = new HashMap(); - - final List listeners = new ArrayList(); +/** + * Core render loop and drawing functionality. + */ +void draw() { + long drawStart = System.nanoTime(); - public SCMidiInput addListener(SCMidiInputListener l) { - listeners.add(l); - return this; - } + // Draws the simulation and the 2D UI overlay + background(40); - public SCMidiInput removeListener(SCMidiInputListener l) { - listeners.remove(l); - return this; - } - - SCMidiInput(MidiInputDevice d) { - mode = MIDI; - d.createInput(this); - name = d.getName(); - } - - SCMidiInput(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; - } - } + color[] simulationColors; + color[] sendColors; + simulationColors = sendColors = glucose.getColors(); + String displayMode = uiCrossfader.getDisplayMode(); + if (displayMode == "A") { + simulationColors = lx.engine.getDeck(GLucose.LEFT_DECK).getColors(); + } else if (displayMode == "B") { + simulationColors = lx.engine.getDeck(GLucose.RIGHT_DECK).getColors(); } - - public boolean isEnabled() { - return enabled; + if (debugMode) { + debugUI.maskColors(simulationColors); + debugUI.maskColors(sendColors); } - - public boolean isSelected() { - return enabled; + + long simulationStart = System.nanoTime(); + if (simulationOn) { + drawSimulation(simulationColors); } + long simulationNanos = System.nanoTime() - simulationStart; - public void onMousePressed() { - setEnabled(!enabled); - } + // 2D Overlay UI + long uiStart = System.nanoTime(); + drawUI(); + long uiNanos = System.nanoTime() - uiStart; - public SCMidiInput setEnabled(boolean enabled) { - if (enabled != this.enabled) { - this.enabled = enabled; - for (SCMidiInputListener l : listeners) { - l.onEnabled(this, enabled); - } - } - return this; + 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; - private SCPattern getFocusedPattern() { - Engine.Deck focusedDeck = (uiMidi != null) ? uiMidi.getFocusedDeck() : lx.engine.getDefaultDeck(); - return (SCPattern) focusedDeck.getActivePattern(); + long sendStart = System.nanoTime(); + for (PandaDriver p : pandaBoards) { + p.send(sendColors); } + long sendNanos = System.nanoTime() - sendStart; - private boolean logMidi() { - return (uiMidi != null) && uiMidi.logMidi(); - } + long drawNanos = System.nanoTime() - drawStart; - void programChangeReceived(ProgramChange pc) { - if (!enabled) { - return; - } - if (logMidi()) { - println(getLabel() + " :: Program Change :: " + pc.getNumber()); - } + if (diagnosticsOn) { + drawDiagnostics(drawNanos, simulationNanos, uiNanos, gammaNanos, sendNanos); } - - void controllerChangeReceived(rwmidi.Controller cc) { - if (!enabled) { - return; - } - if (logMidi()) { - println(getLabel() + " :: Controller :: " + cc.getCC() + ":" + cc.getValue()); - } - getFocusedPattern().controllerChangeReceived(cc); - } - - void noteOnReceived(Note note) { - if (!enabled) { - return; - } - if (logMidi()) { - println(getLabel() + " :: Note On :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity()); - } - getFocusedPattern().noteOnReceived(note); - } - - void noteOffReceived(Note note) { - if (!enabled) { - return; - } - if (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); - 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(); +void drawDiagnostics(long drawNanos, long simulationNanos, long uiNanos, long gammaNanos, long sendNanos) { + 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(); + int xp = x; + float hv = 0; + for (long val : new long[] {lx.timer.drawNanos, simulationNanos, uiNanos, gammaNanos, sendNanos }) { + fill(lx.hsb(hv % 360, 100, 80)); + rect(xp, y, val * ws, h-1); + hv += 140; + xp += val * ws; } - if (debugMode) { - debugUI.maskColors(colors); + 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(); + 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; } +} +void drawSimulation(color[] simulationColors) { camera( eyeX, eyeY, eyeZ, midX, midY, midZ, @@ -438,10 +350,12 @@ void draw() { popMatrix(); 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); } @@ -449,51 +363,25 @@ void draw() { 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 (LXPoint p : glucose.model.points) { + stroke(simulationColors[p.index]); + vertex(p.x, p.y, p.z); } 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 - 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. - ); - } - - // TODO(mcslee): move into GLucose engine - for (PandaDriver p : pandaBoards) { - p.send(colors); - } } -void drawBassBox(BassBox b) { +void drawBassBox(BassBox b, boolean hasSub) { + 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); @@ -624,6 +512,44 @@ void keyPressed() { 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); @@ -631,15 +557,18 @@ void keyPressed() { case '=': case '+': frameRate(++targetFramerate); - break; + break; + case 'b': + effects.boom.trigger(); + break; case 'd': - if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) { + if (!midiEngine.isQwertyEnabled()) { 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); @@ -655,13 +584,28 @@ void keyPressed() { } } break; + case 't': + if (!midiEngine.isQwertyEnabled()) { + lx.engine.setThreaded(!lx.engine.isThreaded()); + } + break; case 'p': for (PandaDriver p : pandaBoards) { p.toggle(); } break; + case 'q': + if (!midiEngine.isQwertyEnabled()) { + diagnosticsOn = !diagnosticsOn; + } + break; + case 's': + if (!midiEngine.isQwertyEnabled()) { + simulationOn = !simulationOn; + } + break; case 'u': - if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) { + if (!midiEngine.isQwertyEnabled()) { uiOn = !uiOn; } break; @@ -708,7 +652,7 @@ void mouseReleased() { context.mouseReleased(mouseX, mouseY); } } - + void mouseWheel(int delta) { boolean wheeled = false; for (UIContext context : overlays) {