X-Git-Url: https://git.piment-noir.org/?p=SugarCubes.git;a=blobdiff_plain;f=_MIDI.pde;h=7673f7ffd87d6cadf3630ed7abfa64efc0e50c7e;hp=b0b8fc4ca9bd46e650c168881601edbf5478162e;hb=8f4e6c99775f2724edf3cec488860eb68b06491c;hpb=6b163a98976a5f2adf9da1b7bd3297e39d6dd7bf diff --git a/_MIDI.pde b/_MIDI.pde index b0b8fc4..7673f7f 100644 --- a/_MIDI.pde +++ b/_MIDI.pde @@ -45,19 +45,41 @@ class MidiEngine { grid = new GridController(this); midiControllers.add(midiQwertyKeys = new VirtualKeyMidiInput(this, VirtualKeyMidiInput.KEYS)); midiControllers.add(midiQwertyAPC = new VirtualKeyMidiInput(this, VirtualKeyMidiInput.APC)); + int apcCount = 0; for (MidiInputDevice device : RWMidi.getInputDevices()) { if (device.getName().contains("APC")) { - midiControllers.add(new APC40MidiInput(this, device).setEnabled(true)); + ++apcCount; + } + } + + int apcIndex = 0; + for (MidiInputDevice device : RWMidi.getInputDevices()) { + if (device.getName().contains("APC")) { + int apcDeck = -1; + if (apcCount > 1 && apcIndex < 2) { + apcDeck = apcIndex++; + } + midiControllers.add(new APC40MidiInput(this, device, apcDeck).setEnabled(true)); } else if (device.getName().contains("SLIDER/KNOB KORG")) { midiControllers.add(new KorgNanoKontrolMidiInput(this, device).setEnabled(true)); + } else if (device.getName().contains("Arturia MINILAB")) { + midiControllers.add(new ArturiaMinilabMidiInput(this, device).setEnabled(true)); } else { - boolean enabled = device.getName().contains("KEYBOARD KORG") || device.getName().contains("Bus 1 Apple"); + boolean enabled = + device.getName().contains("KEYBOARD KORG") || + device.getName().contains("Bus 1 Apple"); midiControllers.add(new GenericDeviceMidiInput(this, device).setEnabled(enabled)); } } + + apcIndex = 0; for (MidiOutputDevice device : RWMidi.getOutputDevices()) { if (device.getName().contains("APC")) { - new APC40MidiOutput(this, device); + int apcDeck = -1; + if (apcCount > 1 && apcIndex < 2) { + apcDeck = apcIndex++; + } + new APC40MidiOutput(this, device, apcDeck); } } } @@ -66,7 +88,7 @@ class MidiEngine { return this.midiControllers; } - public Engine.Deck getFocusedDeck() { + public LXDeck getFocusedDeck() { return lx.engine.getDeck(activeDeckIndex); } @@ -146,6 +168,10 @@ public abstract class SCMidiInput extends AbstractScrollItem { private boolean logMidi() { return (uiMidi != null) && uiMidi.logMidi(); } + + protected SCPattern getTargetPattern() { + return midiEngine.getFocusedPattern(); + } final void programChangeReceived(ProgramChange pc) { if (!enabled) { @@ -164,10 +190,8 @@ public abstract class SCMidiInput extends AbstractScrollItem { if (logMidi()) { println(getLabel() + " :: Controller :: " + cc.getChannel() + " :: " + cc.getCC() + ":" + cc.getValue()); } - if (!handleGridControllerChange(cc)) { - if (!midiEngine.getFocusedPattern().controllerChange(cc)) { - handleControllerChange(cc); - } + if (!handleControllerChange(cc)) { + getTargetPattern().controllerChange(cc); } } @@ -178,10 +202,8 @@ public abstract class SCMidiInput extends AbstractScrollItem { if (logMidi()) { println(getLabel() + " :: Note On :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity()); } - if (!handleGridNoteOn(note)) { - if (!midiEngine.getFocusedPattern().noteOn(note)) { - handleNoteOn(note); - } + if (!handleNoteOn(note)) { + getTargetPattern().noteOn(note); } } @@ -192,21 +214,26 @@ public abstract class SCMidiInput extends AbstractScrollItem { if (logMidi()) { println(getLabel() + " :: Note Off :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity()); } - if (!handleGridNoteOff(note)) { - if (!midiEngine.getFocusedPattern().noteOff(note)) { - handleNoteOff(note); + if (!handleNoteOff(note)) { + getTargetPattern().noteOff(note); + } + } + + protected void setNormalized(LXParameter parameter, float value) { + if (parameter != null) { + if (parameter instanceof BasicParameter) { + ((BasicParameter)parameter).setNormalized(value); + } else { + parameter.setValue(value); } } } // Subclasses may implement these to map top-level functionality - protected boolean handleGridNoteOn(Note note) { return false; } - protected boolean handleGridNoteOff(Note note) { return false; } - protected boolean handleGridControllerChange(rwmidi.Controller cc) { return false; } - protected void handleProgramChange(ProgramChange pc) {} - protected void handleControllerChange(rwmidi.Controller cc) {} - protected void handleNoteOn(Note note) {} - protected void handleNoteOff(Note note) {} + protected boolean handleProgramChange(ProgramChange pc) { return false; } + protected boolean handleControllerChange(rwmidi.Controller cc) { return false; } + protected boolean handleNoteOn(Note note) { return false; } + protected boolean handleNoteOff(Note note) { return false; } } public class VirtualKeyMidiInput extends SCMidiInput { @@ -330,9 +357,26 @@ public class APC40MidiInput extends GenericDeviceMidiInput { private boolean shiftOn = false; private LXEffect releaseEffect = null; + final private LXDeck targetDeck; APC40MidiInput(MidiEngine midiEngine, MidiInputDevice d) { + this(midiEngine, d, -1); + } + + APC40MidiInput(MidiEngine midiEngine, MidiInputDevice d, int deckIndex) { super(midiEngine, d); + targetDeck = (deckIndex < 0) ? null : lx.engine.getDecks().get(deckIndex); + } + + protected LXDeck getTargetDeck() { + return (targetDeck != null) ? targetDeck : midiEngine.getFocusedDeck(); + } + + protected SCPattern getTargetPattern() { + if (targetDeck != null) { + return (SCPattern) (targetDeck.getActivePattern()); + } + return super.getTargetPattern(); } private class GridPosition { @@ -348,12 +392,11 @@ public class APC40MidiInput extends GenericDeviceMidiInput { int pitch = note.getPitch(); if (channel < 8) { if (pitch >= 53 && pitch <=57) return new GridPosition(pitch-53, channel); - else if (pitch == 52) return new GridPosition(5, channel); } return null; } - protected boolean handleGridNoteOn(Note note) { + private boolean handleGridNoteOn(Note note) { GridPosition p = getGridPosition(note); if (p != null) { return midiEngine.grid.gridPressed(p.row, p.col); @@ -361,7 +404,7 @@ public class APC40MidiInput extends GenericDeviceMidiInput { return false; } - protected boolean handleGridNoteOff(Note note) { + private boolean handleGridNoteOff(Note note) { GridPosition p = getGridPosition(note); if (p != null) { return midiEngine.grid.gridReleased(p.row, p.col); @@ -369,13 +412,54 @@ public class APC40MidiInput extends GenericDeviceMidiInput { return false; } - protected void handleControllerChange(rwmidi.Controller cc) { + protected boolean handleControllerChange(rwmidi.Controller cc) { + int channel = cc.getChannel(); int number = cc.getCC(); + float value = cc.getValue() / 127.; switch (number) { + + case 7: + switch (channel) { + case 0: + uiSpeed.speed.setNormalized(0.5 - value*0.5); + return true; + case 1: + effects.colorFucker.desat.setNormalized(value); + return true; + case 2: + effects.colorFucker.desat.setNormalized(value); + return true; + case 3: + effects.blur.amount.setNormalized(value); + return true; + case 4: + effects.quantize.amount.setNormalized(value); + return true; + } + break; + + // Master bright + case 14: + effects.colorFucker.level.setNormalized(value); + return true; + // Crossfader case 15: - lx.engine.getDeck(1).getCrossfader().setValue(cc.getValue() / 127.); - break; + lx.engine.getDeck(GLucose.RIGHT_DECK).getFader().setNormalized(value); + return true; + + // Cue level + case 47: + float val = effects.colorFucker.hueShift.getValuef(); + int cv = cc.getValue(); + if (cv < 64) { + cv = 64 + cv; + } else { + cv = cv - 64; + } + val += (cv - 64) / 500.; + effects.colorFucker.hueShift.setNormalized((val+1) % 1); + return true; } int parameterIndex = -1; @@ -385,9 +469,10 @@ public class APC40MidiInput extends GenericDeviceMidiInput { parameterIndex = 8 + (number-16); } if (parameterIndex >= 0) { - List parameters = midiEngine.getFocusedPattern().getParameters(); + List parameters = getTargetPattern().getParameters(); if (parameterIndex < parameters.size()) { - parameters.get(parameterIndex).setValue(cc.getValue() / 127.); + setNormalized(parameters.get(parameterIndex), value); + return true; } } @@ -395,9 +480,12 @@ public class APC40MidiInput extends GenericDeviceMidiInput { int effectIndex = number - 20; List parameters = glucose.getSelectedEffect().getParameters(); if (effectIndex < parameters.size()) { - parameters.get(effectIndex).setValue(cc.getValue() / 127.); + setNormalized(parameters.get(effectIndex), value); + return true; } } + + return false; } private long tap1 = 0; @@ -406,62 +494,98 @@ public class APC40MidiInput extends GenericDeviceMidiInput { return a >= b && a <= c; } - protected void handleNoteOn(Note note) { - int nPitch = note.getPitch(), nChan = note.getChannel(); + protected boolean handleNoteOn(Note note) { + if (handleGridNoteOn(note)) { + return true; + } + + int nPitch = note.getPitch(); + int nChan = note.getChannel(); switch (nPitch) { - - case 82: // scene 1 - EFF_boom.trigger(); + + case 49: // SOLO/CUE + switch (nChan) { + case 4: + effects.colorFucker.mono.setNormalized(1); + return true; + case 5: + effects.colorFucker.invert.setNormalized(1); + return true; + case 6: + lx.cycleBaseHue(60000); + return true; + } break; + + case 82: // scene 1 + effects.boom.trigger(); + return true; case 83: // scene 2 - EFF_flash.trigger(); - break; + effects.flash.trigger(); + return true; + + case 84: // scene 3 + getTargetPattern().reset(); + return true; case 90: // dan's dirty tapping mechanism lx.tempo.trigger(); tap1 = millis(); - break; + return true; case 91: // play - case 97: // left bank - midiEngine.setFocusedDeck(0); - break; - + if (shiftOn) { + midiEngine.setFocusedDeck(GLucose.LEFT_DECK); + } else { + uiCrossfader.setDisplayMode("A"); + } + return true; + + case 92: // stop + uiCrossfader.setDisplayMode("COMP"); + return true; + case 93: // rec - case 96: // right bank - midiEngine.setFocusedDeck(1); - break; + if (shiftOn) { + midiEngine.setFocusedDeck(GLucose.RIGHT_DECK); + } else { + uiCrossfader.setDisplayMode("B"); + } + return true; case 94: // up bank if (shiftOn) { glucose.incrementSelectedEffectBy(-1); } else { - midiEngine.getFocusedDeck().goPrev(); + getTargetDeck().goPrev(); } - break; + return true; + case 95: // down bank if (shiftOn) { glucose.incrementSelectedEffectBy(1); } else { - midiEngine.getFocusedDeck().goNext(); + getTargetDeck().goNext(); } - break; + return true; case 98: // shift shiftOn = true; - break; + return true; case 99: // tap tempo lx.tempo.tap(); - break; + return true; + case 100: // nudge+ lx.tempo.setBpm(lx.tempo.bpm() + (shiftOn ? 1 : .1)); - break; + return true; + case 101: // nudge- lx.tempo.setBpm(lx.tempo.bpm() - (shiftOn ? 1 : .1)); - break; + return true; case 62: // Detail View / red 5 releaseEffect = glucose.getSelectedEffect(); @@ -470,17 +594,50 @@ public class APC40MidiInput extends GenericDeviceMidiInput { } else { releaseEffect.toggle(); } - break; + return true; case 63: // rec quantize / red 6 glucose.getSelectedEffect().disable(); - break; + return true; } + + return false; } - protected void handleNoteOff(Note note) { - int nPitch = note.getPitch(), nChan = note.getChannel(); + protected boolean handleNoteOff(Note note) { + if (handleGridNoteOff(note)) { + return true; + } + + int nPitch = note.getPitch(); + int nChan = note.getChannel(); + switch (nPitch) { + + case 49: // SOLO/CUE + switch (nChan) { + case 4: + effects.colorFucker.mono.setNormalized(0); + return true; + case 5: + effects.colorFucker.invert.setNormalized(0); + return true; + case 6: + lx.setBaseHue(lx.getBaseHue()); + return true; + } + break; + + case 52: // CLIP STOP + if (nChan < PresetManager.NUM_PRESETS) { + if (shiftOn) { + presetManager.store(getTargetDeck(), nChan); + } else { + presetManager.select(getTargetDeck(), nChan); + } + } + return true; + case 90: // SEND C long tapDelta = millis() - tap1; if (lbtwn(tapDelta,5000,300*1000)) { // hackish tapping mechanism @@ -492,7 +649,7 @@ public class APC40MidiInput extends GenericDeviceMidiInput { tap1 = 0; println("Tap Set - " + bpm + " bpm"); } - break; + return true; case 63: // rec quantize / RED 6 if (releaseEffect != null) { @@ -500,12 +657,14 @@ public class APC40MidiInput extends GenericDeviceMidiInput { releaseEffect.disable(); } } - break; + return true; case 98: // shift shiftOn = false; - break; + return true; } + + return false; } } @@ -515,69 +674,133 @@ class KorgNanoKontrolMidiInput extends GenericDeviceMidiInput { super(midiEngine, d); } - protected void handleControllerChange(rwmidi.Controller cc) { + protected boolean handleControllerChange(rwmidi.Controller cc) { int number = cc.getCC(); if (number >= 16 && number <= 23) { int parameterIndex = number - 16; List parameters = midiEngine.getFocusedPattern().getParameters(); if (parameterIndex < parameters.size()) { - parameters.get(parameterIndex).setValue(cc.getValue() / 127.); + setNormalized(parameters.get(parameterIndex), cc.getValue() / 127.); + return true; } } if (cc.getValue() == 127) { switch (number) { - // Left track - case 58: - midiEngine.setFocusedDeck(0); - break; - // Right track - case 59: - midiEngine.setFocusedDeck(1); - break; - // Left chevron - case 43: + + case 58: // Left track + midiEngine.setFocusedDeck(GLucose.LEFT_DECK); + return true; + + case 59: // Right track + midiEngine.setFocusedDeck(GLucose.RIGHT_DECK); + return true; + + case 43: // Left chevron midiEngine.getFocusedDeck().goPrev(); - break; - // Right chevron - case 44: + return true; + + case 44: // Right chevron midiEngine.getFocusedDeck().goNext(); - break; + return true; } } + + return false; } } -class APC40MidiOutput implements LXParameter.Listener, GridOutput { +class APC40MidiOutput implements LXParameterListener, GridOutput { private final MidiEngine midiEngine; private final MidiOutput output; private LXPattern focusedPattern = null; private LXEffect focusedEffect = null; + private final LXDeck targetDeck; APC40MidiOutput(MidiEngine midiEngine, MidiOutputDevice device) { + this(midiEngine, device, -1); + } + + APC40MidiOutput(MidiEngine midiEngine, MidiOutputDevice device, int deckIndex) { this.midiEngine = midiEngine; output = device.createOutput(); - midiEngine.addListener(new MidiEngineListener() { - public void onFocusedDeck(int deckIndex) { - resetPatternParameters(); - } - }); + targetDeck = (deckIndex < 0) ? null : lx.engine.getDecks().get(deckIndex); + setDPatternOutputs(); + if (targetDeck != null) { + midiEngine.addListener(new MidiEngineListener() { + public void onFocusedDeck(int deckIndex) { + resetPatternParameters(); + for (int i = 0; i < 8; ++i) { + output.sendNoteOn(i, 52, 0); + } + } + }); + } glucose.addEffectListener(new GLucose.EffectListener() { public void effectSelected(LXEffect effect) { resetEffectParameters(); } }); - Engine.Listener deckListener = new Engine.AbstractListener() { - public void patternDidChange(Engine.Deck deck, LXPattern pattern) { - resetPatternParameters(); + LXDeck.Listener deckListener = new LXDeck.AbstractListener() { + public void patternDidChange(LXDeck deck, LXPattern pattern) { + if (deck == getTargetDeck()) { + resetPatternParameters(); + } } }; - for (Engine.Deck d : lx.engine.getDecks()) { - d.addListener(deckListener); + for (LXDeck d : lx.engine.getDecks()) { + if (targetDeck == null || d == targetDeck) { + d.addListener(deckListener); + } } + presetManager.addListener(new PresetListener() { + public void onPresetSelected(LXDeck deck, Preset preset) { + if (deck == getTargetDeck()) { + for (int i = 0; i < 8; ++i) { + output.sendNoteOn(i, 52, (preset.index == i) ? 1 : 0); + } + } + } + public void onPresetDirty(LXDeck deck, Preset preset) { + if (deck == getTargetDeck()) { + output.sendNoteOn(preset.index, 52, 2); + } + } + public void onPresetStored(LXDeck deck, Preset preset) { + onPresetSelected(deck, preset); + } + }); resetParameters(); midiEngine.grid.addOutput(this); + + lx.cycleBaseHue(60000); + output.sendNoteOn(6, 49, 127); + + // Turn off the track selection lights and preset selectors + for (int i = 0; i < 8; ++i) { + output.sendNoteOn(i, 51, 0); + output.sendNoteOn(i, 52, 0); + } + + // Turn off the MASTER selector + output.sendNoteOn(0, 80, 0); + } + + private void setDPatternOutputs() { + for (LXDeck deck : lx.engine.getDecks()) { + if (targetDeck == null || deck == targetDeck) { + for (LXPattern pattern : deck.getPatterns()) { + if (pattern instanceof DPat) { + ((DPat)pattern).setAPCOutput(output); + } + } + } + } + } + + protected LXDeck getTargetDeck() { + return (targetDeck != null) ? targetDeck : midiEngine.getFocusedDeck(); } private void resetParameters() { @@ -586,7 +809,7 @@ class APC40MidiOutput implements LXParameter.Listener, GridOutput { } private void resetPatternParameters() { - LXPattern newPattern = midiEngine.getFocusedDeck().getActivePattern(); + LXPattern newPattern = getTargetDeck().getActivePattern(); if (newPattern == focusedPattern) { return; } @@ -604,9 +827,16 @@ class APC40MidiOutput implements LXParameter.Listener, GridOutput { while (i < 12) { sendKnob(i++, 0); } - for (int row = 0; row < 7; ++row) { - for (int col = 0; col < 8; ++col) { - setGridState(row, col, 0); + if (focusedPattern instanceof DPat) { + ((DPat)focusedPattern).updateLights(); + } else { + for (int j = 0; j < 8; ++j) { + output.sendNoteOn(j, 48, 0); + } + for (int row = 0; row < 7; ++row) { + for (int col = 0; col < 8; ++col) { + setGridState(row, col, 0); + } } } } @@ -664,13 +894,48 @@ class APC40MidiOutput implements LXParameter.Listener, GridOutput { } public void setGridState(int row, int col, int state) { - if (col < 8) { - if (row < 5) output.sendNoteOn(col, 53+row, state); - else if (row == 6) output.sendNoteOn(col, 52, state); + if (col < 8 && row < 5) { + output.sendNoteOn(col, 53+row, state); } } } +class ArturiaMinilabMidiInput extends GenericDeviceMidiInput { + ArturiaMinilabMidiInput(MidiEngine midiEngine, MidiInputDevice d) { + super(midiEngine, d); + } + + protected boolean handleControllerChange(rwmidi.Controller cc) { + int parameterIndex = -1; + switch (cc.getCC()) { + case 7: parameterIndex = 0; break; + case 74: parameterIndex = 1; break; + case 71: parameterIndex = 2; break; + case 76: parameterIndex = 3; break; + case 114: parameterIndex = 4; break; + case 18: parameterIndex = 5; break; + case 19: parameterIndex = 6; break; + case 16: parameterIndex = 7; break; + + case 75: + float val = effects.colorFucker.hueShift.getValuef(); + val += (cc.getValue() - 64) / 256.; + effects.colorFucker.hueShift.setNormalized((val+1) % 1); + break; + } + if (parameterIndex >= 0) { + List parameters = midiEngine.getFocusedPattern().getParameters(); + if (parameterIndex < parameters.size()) { + LXParameter p = parameters.get(parameterIndex); + float curVal = p.getValuef(); + curVal += (cc.getValue() - 64) / 127.; + setNormalized(p, constrain(curVal, 0, 1)); + } + } + return false; + } +} + interface GridOutput { public static final int OFF = 0; public static final int GREEN = 1;