From 2b068dd5d83e070da10318a9ab4017a7ce86d2ca Mon Sep 17 00:00:00 2001 From: Mark Slee Date: Mon, 21 Oct 2013 18:05:38 -0700 Subject: [PATCH] New preset listening code and keyboard buttons for presets --- _Internals.pde | 39 +++++++++++- _MIDI.pde | 21 +++---- _Presets.pde | 156 ++++++++++++++++++++++++----------------------- code/HeronLX.jar | Bin 68996 -> 69191 bytes 4 files changed, 128 insertions(+), 88 deletions(-) diff --git a/_Internals.pde b/_Internals.pde index 2e91381..79d12e5 100644 --- a/_Internals.pde +++ b/_Internals.pde @@ -140,7 +140,6 @@ void setup() { // MIDI devices midiEngine = new MidiEngine(); - presetManager.setMidiEngine(midiEngine); logTime("Setup MIDI devices"); // Build output driver @@ -425,6 +424,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); diff --git a/_MIDI.pde b/_MIDI.pde index 8f71457..5747a1f 100644 --- a/_MIDI.pde +++ b/_MIDI.pde @@ -677,6 +677,9 @@ class APC40MidiOutput implements LXParameter.Listener, GridOutput { midiEngine.addListener(new MidiEngineListener() { public void onFocusedDeck(int deckIndex) { resetPatternParameters(); + for (int i = 0; i < 8; ++i) { + output.sendNoteOn(i, 52, 0); + } } }); } @@ -687,7 +690,9 @@ class APC40MidiOutput implements LXParameter.Listener, GridOutput { }); LXDeck.Listener deckListener = new LXDeck.AbstractListener() { public void patternDidChange(LXDeck deck, LXPattern pattern) { - resetPatternParameters(); + if (deck == getTargetDeck()) { + resetPatternParameters(); + } } }; for (LXDeck d : lx.engine.getDecks()) { @@ -696,7 +701,7 @@ class APC40MidiOutput implements LXParameter.Listener, GridOutput { } } presetManager.addListener(new PresetListener() { - public void onPresetLoaded(LXDeck deck, Preset preset) { + 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); @@ -710,12 +715,7 @@ class APC40MidiOutput implements LXParameter.Listener, GridOutput { } public void onPresetStored(LXDeck deck, Preset preset) { if (deck == getTargetDeck()) { - onPresetLoaded(deck, preset); - } - } - public void onPresetUnloaded() { - for (int i = 0; i < 8; ++i) { - output.sendNoteOn(i, 52, 0); + onPresetStored(deck, preset); } } }); @@ -748,10 +748,7 @@ class APC40MidiOutput implements LXParameter.Listener, GridOutput { } protected LXDeck getTargetDeck() { - if (targetDeck != null) { - return targetDeck; - } - return midiEngine.getFocusedDeck(); + return (targetDeck != null) ? targetDeck : midiEngine.getFocusedDeck(); } private void resetParameters() { diff --git a/_Presets.pde b/_Presets.pde index 2c61125..9d5c305 100644 --- a/_Presets.pde +++ b/_Presets.pde @@ -1,22 +1,77 @@ interface PresetListener { - public void onPresetLoaded(LXDeck deck, Preset preset); - public void onPresetDirty(LXDeck deck, Preset preset); + public void onPresetSelected(LXDeck deck, Preset preset); public void onPresetStored(LXDeck deck, Preset preset); - public void onPresetUnloaded(); + public void onPresetDirty(LXDeck deck, Preset preset); } -class PresetManager implements LXParameter.Listener { +class PresetManager { public static final int NUM_PRESETS = 8; public static final String FILENAME = "data/presets.txt"; public static final String DELIMITER = "\t"; + class DeckState implements LXParameter.Listener { + + final LXDeck deck; + LXPattern selectedPattern = null; + Preset selectedPreset = null; + boolean isDirty = false; + + DeckState(LXDeck deck) { + this.deck = deck; + deck.addListener(new LXDeck.AbstractListener() { + public void patternDidChange(LXDeck deck, LXPattern pattern) { + if (selectedPattern != pattern) { + onDirty(); + } + } + }); + } + + private void onSelect(Preset preset, LXPattern pattern) { + if ((selectedPattern != pattern) && (selectedPattern != null)) { + for (LXParameter p : selectedPattern.getParameters()) { + ((LXListenableParameter) p).removeListener(this); + } + } + selectedPreset = preset; + selectedPattern = pattern; + isDirty = false; + for (LXParameter p : pattern.getParameters()) { + ((LXListenableParameter) p).addListener(this); + } + for (PresetListener listener : listeners) { + listener.onPresetSelected(deck, preset); + } + } + + private void onStore(Preset preset, LXPattern pattern) { + selectedPreset = preset; + selectedPattern = pattern; + isDirty = false; + for (PresetListener listener : listeners) { + listener.onPresetStored(deck, preset); + } + } + + private void onDirty() { + if (selectedPreset != null) { + isDirty = true; + for (PresetListener listener : listeners) { + listener.onPresetDirty(deck, selectedPreset); + } + } + } + + public void onParameterChanged(LXParameter parameter) { + onDirty(); + } + } + + private final DeckState[] deckState = new DeckState[lx.engine.getDecks().size()]; private final Preset[] presets = new Preset[NUM_PRESETS]; private final List listeners = new ArrayList(); - private Preset loadedPreset = null; - private LXPattern loadedPattern = null; - PresetManager() { for (int i = 0; i < presets.length; ++i) { presets[i] = new Preset(this, i); @@ -34,88 +89,38 @@ class PresetManager implements LXParameter.Listener { } } for (LXDeck deck : lx.engine.getDecks()) { - deck.addListener(new LXDeck.AbstractListener() { - public void patternDidChange(LXDeck deck, LXPattern pattern) { - if (pattern != loadedPattern) { - onPresetDirty(deck); - } - } - }); + deckState[deck.index] = new DeckState(deck); } } - public void setMidiEngine(MidiEngine midiEngine) { - midiEngine.addListener(new MidiEngineListener() { - public void onFocusedDeck(int deckIndex) { - loadedPreset = null; - for (PresetListener listener : listeners) { - listener.onPresetUnloaded(); - } - } - }); - } - public void addListener(PresetListener listener) { listeners.add(listener); } - public LXDeck deckForPattern(LXPattern pattern) { - for (LXDeck deck : lx.engine.getDecks()) { - for (LXPattern p : deck.getPatterns()) { - if (p == pattern) { - return deck; - } - } - } - return null; - } - - public void dirty(LXPattern pattern) { - onPresetDirty(deckForPattern(pattern)); - } - public void select(LXDeck deck, int index) { presets[index].select(deck); } public void store(LXDeck deck, int index) { - presets[index].store(midiEngine.getFocusedPattern()); - for (PresetListener listener : listeners) { - listener.onPresetStored(deck, presets[index]); - } - select(deck, index); + presets[index].store(deck); } - public void onPresetLoaded(LXDeck deck, Preset preset, LXPattern pattern) { - if (loadedPattern != pattern) { - if (loadedPattern != null) { - for (LXParameter p : loadedPattern.getParameters()) { - ((LXListenableParameter) p).removeListener(this); - } - } - } - for (PresetListener listener : listeners) { - listener.onPresetLoaded(deck, preset); - } - loadedPreset = preset; - loadedPattern = pattern; - for (LXParameter p : loadedPattern.getParameters()) { - ((LXListenableParameter) p).addListener(this); - } + public void dirty(LXDeck deck) { + deckState[deck.index].onDirty(); } - private void onPresetDirty(LXDeck deck) { - if (loadedPreset != null) { - for (PresetListener listener : listeners) { - listener.onPresetDirty(deck, loadedPreset); - } - } + public void dirty(LXPattern pattern) { + dirty(pattern.getDeck()); } - - public void onParameterChanged(LXParameter p) { - onPresetDirty(deckForPattern(loadedPattern)); + + public void onStore(LXDeck deck, Preset preset, LXPattern pattern) { + deckState[deck.index].onStore(preset, pattern); } + public void onSelect(LXDeck deck, Preset preset, LXPattern pattern) { + deckState[deck.index].onSelect(preset, pattern); + } + public void write() { String[] lines = new String[NUM_PRESETS]; int i = 0; @@ -167,10 +172,10 @@ class Preset { return val; } - public void store(LXPattern pattern) { - className = null; - parameters.clear(); + public void store(LXDeck deck) { + LXPattern pattern = deck.getActivePattern(); className = pattern.getClass().getName(); + parameters.clear(); for (LXParameter p : pattern.getParameters()) { parameters.put(p.getLabel(), p.getValuef()); } @@ -184,6 +189,7 @@ class Preset { } } manager.write(); + manager.onStore(deck, this, pattern); } public void select(LXDeck deck) { @@ -214,10 +220,10 @@ class Preset { if (pattern instanceof DPat) { ((DPat)pattern).updateLights(); } - manager.onPresetLoaded(deck, this, pattern); + manager.onSelect(deck, this, pattern); break; } - } + } } } diff --git a/code/HeronLX.jar b/code/HeronLX.jar index eea96270693d7843089f9b7b5da80febe56941d0..c407dc895a188c6787cafea31b0d9ff7a5ebfd39 100755 GIT binary patch delta 9356 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 G8UF|Kie)qa delta 9260 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