2 * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND
5 * ///\\\ ///\\\ ///\\\ ///\\\
6 * \\\/// \\\/// \\\/// \\\///
9 * EXPERTS ONLY!! EXPERTS ONLY!!
11 * This file defines the MIDI mapping interfaces. This shouldn't
12 * need editing unless you're adding top level support for a
13 * specific MIDI device of some sort. Generally, all MIDI devices
14 * will just work with the default configuration, and you can
15 * set your SCPattern class to respond to the controllers that you
19 interface MidiEngineListener {
20 public void onFocusedDeck(int deckIndex);
25 private final List<MidiEngineListener> listeners = new ArrayList<MidiEngineListener>();
26 private final List<SCMidiInput> midiControllers = new ArrayList<SCMidiInput>();
28 public MidiEngine addListener(MidiEngineListener l) {
33 public MidiEngine removeListener(MidiEngineListener l) {
38 private SCMidiInput midiQwertyKeys;
39 private SCMidiInput midiQwertyAPC;
41 private int activeDeckIndex = 0;
45 midiControllers.add(midiQwertyKeys = new SCMidiInput(SCMidiInput.KEYS));
46 midiControllers.add(midiQwertyAPC = new SCMidiInput(SCMidiInput.APC));
47 for (MidiInputDevice device : RWMidi.getInputDevices()) {
48 if (device.getName().contains("APC")) {
49 midiControllers.add(new APC40MidiInput(device).setEnabled(true));
50 } else if (device.getName().contains("SLIDER/KNOB KORG")) {
51 midiControllers.add(new KorgNanoKontrolMidiInput(device).setEnabled(true));
53 midiControllers.add(new SCMidiInput(device));
58 public List<SCMidiInput> getControllers() {
59 return this.midiControllers;
62 public MidiEngine setFocusedDeck(int deckIndex) {
63 if (this.activeDeckIndex != deckIndex) {
64 this.activeDeckIndex = deckIndex;
65 for (MidiEngineListener listener : listeners) {
66 listener.onFocusedDeck(deckIndex);
72 public Engine.Deck getFocusedDeck() {
73 return lx.engine.getDeck(activeDeckIndex);
76 public boolean isQwertyEnabled() {
77 return midiQwertyKeys.isEnabled() || midiQwertyAPC.isEnabled();
81 public interface SCMidiInputListener {
82 public void onEnabled(SCMidiInput controller, boolean enabled);
85 public class SCMidiInput extends AbstractScrollItem {
87 public static final int MIDI = 0;
88 public static final int KEYS = 1;
89 public static final int APC = 2;
91 private boolean enabled = false;
92 private final String name;
93 private final int mode;
94 private int octaveShift = 0;
99 NoteMeta(int channel, int number) {
100 this.channel = channel;
101 this.number = number;
105 final Map<Character, NoteMeta> keyToNote = new HashMap<Character, NoteMeta>();
107 final List<SCMidiInputListener> listeners = new ArrayList<SCMidiInputListener>();
109 public SCMidiInput addListener(SCMidiInputListener l) {
114 public SCMidiInput removeListener(SCMidiInputListener l) {
119 SCMidiInput(MidiInputDevice d) {
122 name = d.getName().replace("Unknown vendor","");
125 SCMidiInput(int mode) {
129 name = "QWERTY (APC Mode)";
134 name = "QWERTY (Key Mode)";
140 private void mapAPC() {
165 registerKeyEvent(this);
168 private void mapKeys() {
170 mapNote('a', 1, note++);
171 mapNote('w', 1, note++);
172 mapNote('s', 1, note++);
173 mapNote('e', 1, note++);
174 mapNote('d', 1, note++);
175 mapNote('f', 1, note++);
176 mapNote('t', 1, note++);
177 mapNote('g', 1, note++);
178 mapNote('y', 1, note++);
179 mapNote('h', 1, note++);
180 mapNote('u', 1, note++);
181 mapNote('j', 1, note++);
182 mapNote('k', 1, note++);
183 mapNote('o', 1, note++);
184 mapNote('l', 1, note++);
185 registerKeyEvent(this);
188 void mapNote(char ch, int channel, int number) {
189 keyToNote.put(ch, new NoteMeta(channel, number));
192 public String getLabel() {
196 public void keyEvent(KeyEvent e) {
200 char c = Character.toLowerCase(e.getKeyChar());
201 NoteMeta nm = keyToNote.get(c);
204 case KeyEvent.KEY_PRESSED:
205 noteOnReceived(new Note(Note.NOTE_ON, nm.channel, nm.number + octaveShift*12, 127));
207 case KeyEvent.KEY_RELEASED:
208 noteOffReceived(new Note(Note.NOTE_OFF, nm.channel, nm.number + octaveShift*12, 0));
212 if ((mode == KEYS) && (e.getID() == KeyEvent.KEY_PRESSED)) {
215 octaveShift = constrain(octaveShift-1, -4, 4);
218 octaveShift = constrain(octaveShift+1, -4, 4);
224 public boolean isEnabled() {
228 public boolean isSelected() {
232 public void onMousePressed() {
233 setEnabled(!enabled);
236 public SCMidiInput setEnabled(boolean enabled) {
237 if (enabled != this.enabled) {
238 this.enabled = enabled;
239 for (SCMidiInputListener l : listeners) {
240 l.onEnabled(this, enabled);
246 protected SCPattern getFocusedPattern() {
247 return (SCPattern) midiEngine.getFocusedDeck().getActivePattern();
250 private boolean logMidi() {
251 return (uiMidi != null) && uiMidi.logMidi();
254 final void programChangeReceived(ProgramChange pc) {
259 println(getLabel() + " :: Program Change :: " + pc.getNumber());
261 handleProgramChange(pc);
264 final void controllerChangeReceived(rwmidi.Controller cc) {
269 println(getLabel() + " :: Controller :: " + cc.getCC() + ":" + cc.getValue());
271 if (!getFocusedPattern().controllerChangeReceived(cc)) {
272 handleControllerChange(cc);
276 final void noteOnReceived(Note note) {
281 println(getLabel() + " :: Note On :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity());
283 if (!getFocusedPattern().noteOnReceived(note)) {
288 final void noteOffReceived(Note note) {
293 println(getLabel() + " :: Note Off :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity());
295 if (!getFocusedPattern().noteOffReceived(note)) {
300 // Subclasses may implement these to map top-level functionality
301 protected void handleProgramChange(ProgramChange pc) {
303 protected void handleControllerChange(rwmidi.Controller cc) {
305 protected void handleNoteOn(Note note) {
307 protected void handleNoteOff(Note note) {
311 public class APC40MidiInput extends SCMidiInput {
313 private boolean shiftOn = false;
314 private LXEffect releaseEffect = null;
316 APC40MidiInput(MidiInputDevice d) {
320 protected void handleControllerChange(rwmidi.Controller cc) {
321 int number = cc.getCC();
325 lx.engine.getDeck(1).getCrossfader().setValue(cc.getValue() / 127.);
329 int parameterIndex = -1;
330 if (number >= 48 && number <= 55) {
331 parameterIndex = number - 48;
332 } else if (number >= 16 && number <= 19) {
333 parameterIndex = 8 + (number-16);
335 if (parameterIndex >= 0) {
336 List<LXParameter> parameters = getFocusedPattern().getParameters();
337 if (parameterIndex < parameters.size()) {
338 parameters.get(parameterIndex).setValue(cc.getValue() / 127.);
342 if (number >= 20 && number <= 23) {
343 int effectIndex = number - 20;
344 List<LXParameter> parameters = glucose.getSelectedEffect().getParameters();
345 if (effectIndex < parameters.size()) {
346 parameters.get(effectIndex).setValue(cc.getValue() / 127.);
351 protected void handleNoteOn(Note note) {
352 switch (note.getPitch()) {
353 case 94: // right bank
354 midiEngine.setFocusedDeck(1);
356 case 95: // left bank
357 midiEngine.setFocusedDeck(0);
361 glucose.incrementSelectedEffectBy(1);
363 midiEngine.getFocusedDeck().goNext();
366 case 97: // down bank
368 glucose.incrementSelectedEffectBy(-1);
370 midiEngine.getFocusedDeck().goPrev();
378 case 99: // tap tempo
382 lx.tempo.setBpm(lx.tempo.bpm() + (shiftOn ? 1 : .1));
385 lx.tempo.setBpm(lx.tempo.bpm() - (shiftOn ? 1 : .1));
390 releaseEffect = glucose.getSelectedEffect();
391 if (releaseEffect.isMomentary()) {
392 releaseEffect.enable();
394 releaseEffect.toggle();
399 glucose.getSelectedEffect().disable();
404 protected void handleNoteOff(Note note) {
405 switch (note.getPitch()) {
407 if (releaseEffect != null) {
408 if (releaseEffect.isMomentary()) {
409 releaseEffect.disable();
421 class KorgNanoKontrolMidiInput extends SCMidiInput {
423 KorgNanoKontrolMidiInput(MidiInputDevice d) {
427 protected void handleControllerChange(rwmidi.Controller cc) {
428 int number = cc.getCC();
429 if (number >= 16 && number <= 23) {
430 int parameterIndex = number - 16;
431 List<LXParameter> parameters = getFocusedPattern().getParameters();
432 if (parameterIndex < parameters.size()) {
433 parameters.get(parameterIndex).setValue(cc.getValue() / 127.);
437 if (cc.getValue() == 127) {
441 midiEngine.setFocusedDeck(0);
445 midiEngine.setFocusedDeck(1);
449 midiEngine.getFocusedDeck().goPrev();
453 midiEngine.getFocusedDeck().goNext();