From 4df91dafc9bd6e090a89cd4ce2dc913d92de435e Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Thu, 19 Sep 2013 19:58:58 -0700 Subject: [PATCH] Add two virtual keyboard modes --- _Internals.pde | 82 ++++++++++++++++++++++++++++++++++++------ _UIFramework.pde | 2 +- _UIImplementation.pde | 6 ++-- code/HeronLX.jar | Bin 64495 -> 64511 bytes 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/_Internals.pde b/_Internals.pde index 044d650..3710e0e 100644 --- a/_Internals.pde +++ b/_Internals.pde @@ -48,7 +48,8 @@ HeronLX lx; LXPattern[] patterns; MappingTool mappingTool; PandaDriver[] pandaBoards; -MidiListener midiQwerty; +MidiListener midiQwertyKeys; +MidiListener midiQwertyAPC; // Display configuration mode boolean mappingMode = false; @@ -123,7 +124,8 @@ void setup() { // MIDI devices List midiListeners = new ArrayList(); - midiListeners.add(midiQwerty = new 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)); @@ -141,8 +143,8 @@ void setup() { 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, 160), - new UIOutput(width-144, 498, 140, 106), + 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), @@ -171,10 +173,16 @@ void setup() { } 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(); } @@ -190,8 +198,25 @@ public class MidiListener extends AbstractScrollItem { final Map keyToNote = new HashMap(); - MidiListener() { - name = "QWERTY Keyboard"; + 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); @@ -219,6 +244,26 @@ public class MidiListener extends AbstractScrollItem { 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)); } @@ -228,15 +273,28 @@ public class MidiListener extends AbstractScrollItem { } 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, 127)); + 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, 0)); + 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; } } @@ -540,13 +598,13 @@ void keyPressed() { frameRate(++targetFramerate); break; case 'd': - if (!midiQwerty.isEnabled()) { + if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) { debugMode = !debugMode; println("Debug output: " + (debugMode ? "ON" : "OFF")); } break; case 'm': - if (!midiQwerty.isEnabled()) { + if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) { mappingMode = !mappingMode; uiPatternA.setVisible(!mappingMode); uiMapping.setVisible(mappingMode); @@ -568,7 +626,9 @@ void keyPressed() { } break; case 'u': - uiOn = !uiOn; + if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) { + uiOn = !uiOn; + } break; } } diff --git a/_UIFramework.pde b/_UIFramework.pde index 2b3d047..0e0be79 100644 --- a/_UIFramework.pde +++ b/_UIFramework.pde @@ -778,7 +778,7 @@ public class UIScrollList extends UIObject { } public void setScrollOffset(int offset) { - scrollOffset = constrain(offset, 0, items.size() - numVisibleItems); + scrollOffset = constrain(offset, 0, max(0, items.size() - numVisibleItems)); scrollYStart = round(scrollOffset * h / items.size()); scrollYHeight = round(numVisibleItems * h / items.size()); redraw(); diff --git a/_UIImplementation.pde b/_UIImplementation.pde index ae61d30..e72d76b 100644 --- a/_UIImplementation.pde +++ b/_UIImplementation.pde @@ -490,9 +490,9 @@ class UIMidi extends UIWindow { for (MidiListener ml : midiListeners) { scrollItems.add(ml); } - new UIScrollList(1, titleHeight, w-2, 80).setItems(scrollItems).addToContainer(this); - (deckMode = new UIToggleSet(4, 110, 90, 20)).setOptions(new String[] { "A", "B" }).addToContainer(this); - (logMode = new UIButton(98, 110, w-103, 20)).setLabel("LOG").addToContainer(this); + new UIScrollList(1, titleHeight, w-2, 100).setItems(scrollItems).addToContainer(this); + (deckMode = new UIToggleSet(4, 130, 90, 20)).setOptions(new String[] { "A", "B" }).addToContainer(this); + (logMode = new UIButton(98, 130, w-103, 20)).setLabel("LOG").addToContainer(this); } public boolean logMidi() { diff --git a/code/HeronLX.jar b/code/HeronLX.jar index 5a98f5e5d93681e73eebac40dc9b8c4768da479e..a3f624137cafd5fdb44e9bd18cd1bcdade1dc611 100755 GIT binary patch delta 6797 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 -- 2.34.1