diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/ArturiaModeLayer.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/ArturiaModeLayer.java index 138a091e..61905768 100644 --- a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/ArturiaModeLayer.java +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/ArturiaModeLayer.java @@ -9,7 +9,7 @@ public class ArturiaModeLayer extends Layer { private final MiniLab3Extension driver; private final NoteInput noteInput; private final Integer[] noteTable = new Integer[128]; - + public ArturiaModeLayer(final MiniLab3Extension driver) { super(driver.getLayers(), "ARTURIA_PAD"); this.driver = driver; @@ -17,35 +17,35 @@ public ArturiaModeLayer(final MiniLab3Extension driver) { noteInput.setShouldConsumeEvents(false); Arrays.fill(noteTable, -1); } - + private void applyNotes() { - final RgbButton[] aButtons = driver.getPadBankAButtons(); - final RgbButton[] bButtons = driver.getPadBankBButtons(); - for (final RgbButton button : aButtons) { + final MinilabRgbButton[] aButtons = driver.getPadBankAButtons(); + final MinilabRgbButton[] bButtons = driver.getPadBankBButtons(); + for (final MinilabRgbButton button : aButtons) { noteTable[button.getNoteValue()] = button.getNoteValue(); } - for (final RgbButton button : bButtons) { + for (final MinilabRgbButton button : bButtons) { noteTable[button.getNoteValue()] = button.getNoteValue(); } noteInput.setKeyTranslationTable(noteTable); } - + protected void resetNotes() { Arrays.fill(noteTable, -1); noteInput.setKeyTranslationTable(noteTable); } - + @Override protected void onActivate() { super.onActivate(); applyNotes(); } - - + + @Override protected void onDeactivate() { super.onDeactivate(); resetNotes(); } - + } diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/ClipLaunchingLayer.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/ClipLaunchingLayer.java index c90a5a75..69d0f6a4 100644 --- a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/ClipLaunchingLayer.java +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/ClipLaunchingLayer.java @@ -2,6 +2,7 @@ import java.util.Arrays; +import com.bitwig.extension.controller.api.BooleanValue; import com.bitwig.extension.controller.api.ClipLauncherSlot; import com.bitwig.extension.controller.api.MultiStateHardwareLight; import com.bitwig.extension.controller.api.Track; @@ -10,35 +11,38 @@ public class ClipLaunchingLayer extends Layer { - private final RgbLightState[] sceneSlotColors = new RgbLightState[8]; + private final RgbLightState[] slotColors = new RgbLightState[8]; + private final BooleanValue clipLauncherOverdub; private final long[] downTime = new long[8]; - private final Layer sceneLaunchingLayer; // clips in selected scene private final TrackBank trackBank; private final MiniLab3Extension driver; private int blinkState; private long clipsStopTiming = 800; + private final BooleanValue transportPlaying; public ClipLaunchingLayer(final MiniLab3Extension driver) { super(driver.getLayers(), "CLIP LAUNCHER"); this.driver = driver; + clipLauncherOverdub = driver.getTransport().isClipLauncherOverdubEnabled(); + transportPlaying = driver.getTransport().isPlaying(); + clipLauncherOverdub.markInterested(); + transportPlaying.markInterested(); - sceneLaunchingLayer = new Layer(driver.getLayers(), "PER_SCENE_LAUNCHER"); - - Arrays.fill(sceneSlotColors, RgbLightState.OFF); + Arrays.fill(slotColors, RgbLightState.OFF); Arrays.fill(downTime, -1); - final RgbButton[] buttons = driver.getPadBankAButtons(); - trackBank = driver.getViewTrackBank(); final MultiStateHardwareLight bankLight = driver.getSurface().createMultiStateHardwareLight("CLIP_LAUNCH_LIGHTS"); bankLight.state().onUpdateHardware(driver::updateBankState); + final MinilabRgbButton[] buttons = driver.getPadBankAButtons(); + final RgbBankLightState.Handler bankLightHandler = new RgbBankLightState.Handler(PadBank.BANK_A, buttons); - sceneLaunchingLayer.bindLightState(bankLightHandler::getBankLightState, bankLight); + this.bindLightState(bankLightHandler::getBankLightState, bankLight); - setupHorizontalLaunching(driver, buttons); + setupHorizontalLaunching(driver); driver.getPadBank().addValueObserver((this::changePadBank)); } @@ -64,31 +68,31 @@ private void changePadBank(final PadBank newValue) { trackBank.setShouldShowClipLauncherFeedback(newValue == PadBank.BANK_A); } - private void setupHorizontalLaunching(final MiniLab3Extension driver, final RgbButton[] buttons) { - driver.getViewTrackBank().setShouldShowClipLauncherFeedback(driver.getPadBank().get() == PadBank.BANK_A); - for (int i = 0; i < MiniLab3Extension.NUM_PADS_TRACK; i++) { - final int index = i; - final Track track = driver.getViewTrackBank().getItemAt(i); - final ClipLauncherSlot slot = track.clipLauncherSlotBank().getItemAt(0); - prepareSlot(slot); + private void setupHorizontalLaunching(final MiniLab3Extension driver) { + final MinilabRgbButton[] buttons = driver.getPadBankAButtons(); + final TrackBank trackBank = driver.getViewTrackBank(); + trackBank.setShouldShowClipLauncherFeedback(driver.getPadBank().get() == PadBank.BANK_A); + for (int i = 0; i < trackBank.getSizeOfBank(); i++) { + final Track track = trackBank.getItemAt(i); + track.arm().markInterested(); + for (int j = 0; j < trackBank.sceneBank().getSizeOfBank(); j++) { + final int buttonIndex = j * 4 + i; + final ClipLauncherSlot slot = track.clipLauncherSlotBank().getItemAt(j); + prepareSlot(slot, buttonIndex); + final MinilabRgbButton button = buttons[buttonIndex]; + button.bindPressed(this, pressed -> handleSlotSelected(buttonIndex, track, slot, pressed)); + button.bindLightState(this, () -> getLightState(buttonIndex, track, slot)); + } track.isQueuedForStop().markInterested(); - - slot.color().addValueObserver((r, g, b) -> sceneSlotColors[index] = RgbLightState.getColor(r, g, b)); - final RgbButton button = buttons[i]; - button.bindPressed(sceneLaunchingLayer, pressed -> handleSlotSelected(index, track, slot, pressed), - () -> getLightState(index, track, slot)); } } - public void navigateScenes(final int direction) { - trackBank.sceneBank().scrollBy(direction); - } - public void launchScene() { trackBank.sceneBank().getScene(0).launch(); } - private void prepareSlot(final ClipLauncherSlot cs) { + private void prepareSlot(final ClipLauncherSlot cs, final int index) { + cs.color().addValueObserver((r, g, b) -> slotColors[index] = RgbLightState.getColor(r, g, b)); cs.exists().markInterested(); cs.hasContent().markInterested(); cs.isPlaybackQueued().markInterested(); @@ -102,9 +106,9 @@ private void prepareSlot(final ClipLauncherSlot cs) { public void notifyBlink(final int blinkState) { this.blinkState = blinkState; final long time = System.currentTimeMillis(); - for (int i = 0; i < 8; i++) { + for (int i = 0; i < trackBank.getSizeOfBank(); i++) { if (downTime[i] != -1 && (time - downTime[i]) > clipsStopTiming) { - final Track track = driver.getViewTrackBank().getItemAt(i); + final Track track = trackBank.getItemAt(i); track.stop(); } } @@ -126,7 +130,10 @@ private void handleSlotSelected(final int slotIndex, final Track track, final Cl } RgbLightState getLightState(final int index, final Track track, final ClipLauncherSlot slot) { - final RgbLightState color = sceneSlotColors[index]; + final RgbLightState color = slotColors[index]; + if (!slot.exists().get()) { + return RgbLightState.OFF; + } if (slot.hasContent().get()) { if (slot.isPlaybackQueued().get()) { return blinkFast(color.getDarker(), color.getBrighter()); @@ -140,13 +147,30 @@ RgbLightState getLightState(final int index, final Track track, final ClipLaunch if (slot.isRecording().get()) { return blinkSlow(color, RgbLightState.RED); } + if (slot.isPlaying().get() && track.isQueuedForStop().get()) { + return blinkSlow(RgbLightState.GREEN, color.getDarker()); + } if (slot.isPlaying().get()) { - return color.getBrighter(); + if (clipLauncherOverdub.get() && track.arm().get()) { + return blinkSlow(RgbLightState.RED, color.getDarker()); + } else { + if (transportPlaying.get()) { + return blinkSlow(RgbLightState.GREEN, color.getBrighter()); + } + return color.getBrighter(); + //return RgbLightState.GREEN; + } } return color.getDarker(); } if (slot.isRecordingQueued().get()) { return blinkFast(color.getDarker(), RgbLightState.RED); + } else if (track.arm().get()) { + return RgbLightState.RED_DIMMED; + } else if (slot.isPlaybackQueued().get()) { + return blinkFast(RgbLightState.GREEN, RgbLightState.WHITE); + } else if (track.isQueuedForStop().get()) { + return blinkFast(RgbLightState.WHITE, RgbLightState.WHITE_DIMMED); } return RgbLightState.OFF; } @@ -168,14 +192,12 @@ private RgbLightState blinkFast(final RgbLightState on, final RgbLightState off) @Override protected void onActivate() { super.onActivate(); - sceneLaunchingLayer.activate(); trackBank.setShouldShowClipLauncherFeedback(driver.getPadBank().get() == PadBank.BANK_A); } @Override protected void onDeactivate() { super.onDeactivate(); - sceneLaunchingLayer.deactivate(); trackBank.setShouldShowClipLauncherFeedback(false); } diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/DrumPadLayer.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/DrumPadLayer.java index 0a5c3ab5..7ba4c86e 100644 --- a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/DrumPadLayer.java +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/DrumPadLayer.java @@ -1,186 +1,198 @@ package com.bitwig.extensions.controllers.arturia.minilab3; -import com.bitwig.extension.controller.api.*; -import com.bitwig.extensions.framework.Layer; -import com.bitwig.extensions.framework.values.Midi; - import java.util.Arrays; import java.util.HashSet; import java.util.Set; -public class DrumPadLayer extends Layer { - private static final String[] noteValues = {"C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "}; - - private final RgbLightState[] colorSlots = new RgbLightState[8]; - //protected final Integer[] velTable = new Integer[128]; - private final Integer[] noteTable = new Integer[128]; - private final boolean[] isPlaying = new boolean[128]; - private final Set padNotes = new HashSet<>(); - private final NoteInput noteInput; - private final DrumPadBank padBank; - private final MiniLab3Extension driver; - private RgbLightState trackColor = RgbLightState.OFF; - private boolean focusOnDrums = false; - private int keysNoteOffset = 60; - private int padsNoteOffset = 32; - private final int[] hangingNotes = new int[8]; - - public DrumPadLayer(final MiniLab3Extension driver) { - super(driver.getLayers(), "DRUM_PAD"); - this.driver = driver; - noteInput = driver.getMidiIn().createNoteInput("MIDI", "89????", "99????", "A9????"); - - noteInput.setShouldConsumeEvents(false); - final PinnableCursorDevice primaryDevice = driver.getPrimaryDevice(); - primaryDevice.hasDrumPads().addValueObserver(this::handleHasDrumsChanged); - padBank = primaryDevice.createDrumPadBank(8); - padBank.scrollPosition().addValueObserver(index -> { - padsNoteOffset = index; - if (isActive()) { - driver.getOled() - .sendTextInfo(DisplayMode.SCENE, "Pad Notes", - String.format("%s - %s", toNote(padsNoteOffset), toNote(padsNoteOffset + 7)), true); - applyNotes(padsNoteOffset); - } - }); - padBank.scrollPosition().set(30); - padBank.setIndication(true); - primaryDevice.hasDrumPads().addValueObserver(hasDrumPads -> { - if (isActive()) { - applyNotes(hasDrumPads ? padsNoteOffset : keysNoteOffset); - } - }); - - padsNoteOffset = padBank.scrollPosition().get(); - Arrays.fill(noteTable, -1); - Arrays.fill(colorSlots, RgbLightState.BLUE); - Arrays.fill(hangingNotes, -1); - - final CursorTrack cursorTrack = driver.getCursorTrack(); - - final RgbButton[] buttons = driver.getPadBankBButtons(); - for (int i = 0; i < buttons.length; i++) { - final int index = i; - padNotes.add(buttons[i].getNoteValue()); - final DrumPad pad = padBank.getItemAt(i); - pad.exists().markInterested(); - - final RgbButton button = buttons[i]; - pad.color().addValueObserver((r, g, b) -> colorSlots[index] = RgbLightState.getColor(r, g, b)); - button.bindPressed(this, down -> handleSelected(index, down), () -> getLightState(index, pad)); - } - - final RgbBankLightState.Handler bankLightHandler = new RgbBankLightState.Handler(PadBank.BANK_B, buttons); - final MultiStateHardwareLight bankLight = driver.getSurface().createMultiStateHardwareLight("PAD_LAUNCH_LIGHTS"); - bankLight.state().onUpdateHardware(driver::updateBankState); - bindLightState(bankLightHandler::getBankLightState, bankLight); - - cursorTrack.playingNotes().addValueObserver(this::handleNotes); - cursorTrack.color().addValueObserver((r, g, b) -> trackColor = RgbLightState.getColor(r, g, b)); - } - - public void notifyNote(final int sb, final int noteValue) { - if (!isActive() || !padNotes.contains(noteValue) || (sb != Midi.NOTE_ON && sb != Midi.NOTE_OFF)) { - return; - } - final int index = noteValue - 44; - final int notePlayed = focusOnDrums ? padsNoteOffset + index : keysNoteOffset + index; - if (sb == Midi.NOTE_ON) { - hangingNotes[index] = notePlayed; - } else { - if (hangingNotes[index] != -1 && hangingNotes[index] != notePlayed) { - noteInput.sendRawMidiEvent(Midi.NOTE_OFF | 9, hangingNotes[index], 0); - } - hangingNotes[index] = -1; - } - } - - private void handleHasDrumsChanged(final boolean hasDrums) { - focusOnDrums = hasDrums; - } - - private void handleNotes(final PlayingNote[] playingNotes) { - if (!isActive()) { - return; - } - Arrays.fill(isPlaying, false); - for (final PlayingNote playingNote : playingNotes) { - isPlaying[playingNote.pitch()] = true; - } - } +import com.bitwig.extension.controller.api.CursorTrack; +import com.bitwig.extension.controller.api.DrumPad; +import com.bitwig.extension.controller.api.DrumPadBank; +import com.bitwig.extension.controller.api.MultiStateHardwareLight; +import com.bitwig.extension.controller.api.NoteInput; +import com.bitwig.extension.controller.api.PinnableCursorDevice; +import com.bitwig.extension.controller.api.PlayingNote; +import com.bitwig.extensions.framework.Layer; +import com.bitwig.extensions.framework.values.Midi; - public void navigate(final int dir) { - if (focusOnDrums) { - driver.getOled().enableValues(DisplayMode.SCENE); - padBank.scrollBy(dir * 4); - } else { - final int newOffset = keysNoteOffset + dir * 4; - if (newOffset >= 0 && newOffset <= 116) { - //muteNoteFromOffset(keysNoteOffset); - keysNoteOffset = newOffset; +public class DrumPadLayer extends Layer { + private static final String[] noteValues = {"C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "}; + + private final RgbLightState[] colorSlots = new RgbLightState[8]; + //protected final Integer[] velTable = new Integer[128]; + private final Integer[] noteTable = new Integer[128]; + private final boolean[] isPlaying = new boolean[128]; + private final Set padNotes = new HashSet<>(); + private final NoteInput noteInput; + private final DrumPadBank padBank; + private final MiniLab3Extension driver; + private RgbLightState trackColor = RgbLightState.OFF; + private boolean focusOnDrums = false; + private int keysNoteOffset = 60; + private int padsNoteOffset = 32; + private final int[] hangingNotes = new int[8]; + + public DrumPadLayer(final MiniLab3Extension driver) { + super(driver.getLayers(), "DRUM_PAD"); + this.driver = driver; + noteInput = driver.getMidiIn().createNoteInput("MIDI", "89????", "99????", "A9????"); + + noteInput.setShouldConsumeEvents(false); + final PinnableCursorDevice primaryDevice = driver.getPrimaryDevice(); + primaryDevice.hasDrumPads().addValueObserver(this::handleHasDrumsChanged); + padBank = primaryDevice.createDrumPadBank(8); + padBank.scrollPosition().addValueObserver(index -> { + padsNoteOffset = index; + if (isActive()) { + driver.getOled() + .sendTextInfo( + DisplayMode.SCENE, "Pad Notes", + String.format("%s - %s", toNote(padsNoteOffset), toNote(padsNoteOffset + 7)), true); + applyNotes(padsNoteOffset); + } + }); + padBank.scrollPosition().set(30); + padBank.setIndication(true); + primaryDevice.hasDrumPads().addValueObserver(hasDrumPads -> { + if (isActive()) { + applyNotes(hasDrumPads ? padsNoteOffset : keysNoteOffset); + } + }); + + padsNoteOffset = padBank.scrollPosition().get(); + Arrays.fill(noteTable, -1); + Arrays.fill(colorSlots, RgbLightState.BLUE); + Arrays.fill(hangingNotes, -1); + + final CursorTrack cursorTrack = driver.getCursorTrack(); + + final MinilabRgbButton[] buttons = driver.getPadBankBButtons(); + for (int i = 0; i < buttons.length; i++) { + final int index = i; + padNotes.add(buttons[i].getNoteValue()); + final DrumPad pad = padBank.getItemAt(i); + pad.exists().markInterested(); + + final MinilabRgbButton button = buttons[i]; + final int slotIndex = driver.getModel() == MinilabModel.MINILAB_3 ? index : (1 - i / 4) * 4 + i % 4; + + pad.color().addValueObserver((r, g, b) -> colorSlots[index] = RgbLightState.getColor(r, g, b)); + button.bindPressed(this, down -> handleSelected(index, down)); + button.bindLightState(this, () -> getLightState(slotIndex, pad)); + } + + final RgbBankLightState.Handler bankLightHandler = new RgbBankLightState.Handler(PadBank.BANK_B, buttons); + final MultiStateHardwareLight bankLight = + driver.getSurface().createMultiStateHardwareLight("PAD_LAUNCH_LIGHTS"); + bankLight.state().onUpdateHardware(driver::updateBankState); + bindLightState(bankLightHandler::getBankLightState, bankLight); + + cursorTrack.playingNotes().addValueObserver(this::handleNotes); + cursorTrack.color().addValueObserver((r, g, b) -> trackColor = RgbLightState.getColor(r, g, b)); + } + + public void notifyNote(final int sb, final int noteValue) { + if (!isActive() || !padNotes.contains(noteValue) || (sb != Midi.NOTE_ON && sb != Midi.NOTE_OFF)) { + return; + } + final int index = noteValue - 44; + final int notePlayed = focusOnDrums ? padsNoteOffset + index : keysNoteOffset + index; + if (sb == Midi.NOTE_ON) { + hangingNotes[index] = notePlayed; + } else { + if (hangingNotes[index] != -1 && hangingNotes[index] != notePlayed) { + noteInput.sendRawMidiEvent(Midi.NOTE_OFF | 9, hangingNotes[index], 0); + } + hangingNotes[index] = -1; + } + } + + private void handleHasDrumsChanged(final boolean hasDrums) { + focusOnDrums = hasDrums; + } + + private void handleNotes(final PlayingNote[] playingNotes) { + if (!isActive()) { + return; + } + Arrays.fill(isPlaying, false); + for (final PlayingNote playingNote : playingNotes) { + isPlaying[playingNote.pitch()] = true; + } + } + + public void navigate(final int dir) { + if (focusOnDrums) { driver.getOled().enableValues(DisplayMode.SCENE); - driver.getOled() - .sendTextInfo(DisplayMode.SCENE, "Pad Notes", - String.format("%s - %s", toNote(newOffset), toNote(newOffset + 7)), true); - applyNotes(keysNoteOffset); - } - } - } - - private String toNote(final int midiValue) { - final int noteValue = midiValue % 12; - final int octave = (midiValue / 12) - 2; - return noteValues[noteValue] + octave; - } - - private void handleSelected(final int index, final boolean pressed) { - driver.getHost().println(String.format("%d %s", index, pressed)); - } - - RgbLightState getLightState(final int index, final DrumPad pad) { - final int noteValue = (focusOnDrums ? padsNoteOffset : keysNoteOffset) + index; - if (noteValue < 128) { - final boolean notePlaying = isPlaying[noteValue]; - if (focusOnDrums) { - if (pad.exists().get()) { - if (colorSlots[index] != null) { - return notePlaying ? colorSlots[index].getBrighter() : colorSlots[index].getDarker(); - } - } else { - return RgbLightState.OFF; + padBank.scrollBy(dir * 4); + } else { + final int newOffset = keysNoteOffset + dir * 4; + if (newOffset >= 0 && newOffset <= 116) { + //muteNoteFromOffset(keysNoteOffset); + keysNoteOffset = newOffset; + driver.getOled().enableValues(DisplayMode.SCENE); + driver.getOled() + .sendTextInfo( + DisplayMode.SCENE, "Pad Notes", + String.format("%s - %s", toNote(newOffset), toNote(newOffset + 7)), true); + applyNotes(keysNoteOffset); + } + } + } + + private String toNote(final int midiValue) { + final int noteValue = midiValue % 12; + final int octave = (midiValue / 12) - 2; + return noteValues[noteValue] + octave; + } + + private void handleSelected(final int index, final boolean pressed) { + driver.getHost().println(String.format("%d %s", index, pressed)); + } + + RgbLightState getLightState(final int index, final DrumPad pad) { + final int noteValue = (focusOnDrums ? padsNoteOffset : keysNoteOffset) + index; + if (noteValue < 128) { + final boolean notePlaying = isPlaying[noteValue]; + if (focusOnDrums) { + if (pad.exists().get()) { + if (colorSlots[index] != null) { + return notePlaying ? colorSlots[index].getBrighter() : colorSlots[index].getDarker(); + } + } else { + return RgbLightState.OFF; + } + return notePlaying ? trackColor.getBrighter() : trackColor.getDarker(); } return notePlaying ? trackColor.getBrighter() : trackColor.getDarker(); - } - return notePlaying ? trackColor.getBrighter() : trackColor.getDarker(); - } else { - return RgbLightState.OFF; - } - } - - public void applyNotes(final int noteOffset) { - Arrays.fill(noteTable, -1); - for (int note = 0; note < 8; note++) { - final int value = noteOffset + note; - noteTable[0x2c + note] = value < 128 ? value : -1; - } - noteInput.setKeyTranslationTable(noteTable); - } - - private void resetNotes() { - Arrays.fill(noteTable, -1); - noteInput.setKeyTranslationTable(noteTable); - } - - @Override - protected void onActivate() { - super.onActivate(); - applyNotes(focusOnDrums ? padsNoteOffset : keysNoteOffset); - } - - @Override - protected void onDeactivate() { - super.onDeactivate(); - resetNotes(); - } - + } else { + return RgbLightState.OFF; + } + } + + public void applyNotes(final int noteOffset) { + Arrays.fill(noteTable, -1); + for (int note = 0; note < 8; note++) { + final int value = noteOffset + note; + noteTable[0x2c + note] = value < 128 ? value : -1; + } + noteInput.setKeyTranslationTable(noteTable); + } + + private void resetNotes() { + Arrays.fill(noteTable, -1); + noteInput.setKeyTranslationTable(noteTable); + } + + @Override + protected void onActivate() { + super.onActivate(); + applyNotes(focusOnDrums ? padsNoteOffset : keysNoteOffset); + } + + @Override + protected void onDeactivate() { + super.onDeactivate(); + resetNotes(); + } + } diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab37ExtensionDefinition.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab37ExtensionDefinition.java new file mode 100644 index 00000000..ca289250 --- /dev/null +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab37ExtensionDefinition.java @@ -0,0 +1,78 @@ +package com.bitwig.extensions.controllers.arturia.minilab3; + +import java.util.UUID; + +import com.bitwig.extension.api.PlatformType; +import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList; +import com.bitwig.extension.controller.api.ControllerHost; + +public class MiniLab37ExtensionDefinition extends MiniLabExtensionDefinition { + private static final UUID DRIVER_ID = UUID.fromString("00d0f8ae-0f13-482f-b96b-8fb016025fcd"); + + private static final String PORT_NAME = "Minilab37"; + + public MiniLab37ExtensionDefinition() { + super(); + } + + @Override + public String getName() { + return "MiniLab 37"; + } + + @Override + public UUID getId() { + return DRIVER_ID; + } + + @Override + public String getHardwareModel() { + return "MiniLab 37"; + } + + @Override + public int getNumMidiInPorts() { + return 2; + } + + @Override + public int getNumMidiOutPorts() { + return 2; + } + + @Override + public void listAutoDetectionMidiPortNames(final AutoDetectionMidiPortNamesList list, + final PlatformType platformType) { + if (platformType == PlatformType.WINDOWS) { + for (int i = 1; i < 5; i++) { + appendWin11Prefix(list, i); + } + } else if (platformType == PlatformType.MAC) { + for (int i = 1; i < 5; i++) { + appendWin11Prefix(list, i); + } + } else if (platformType == PlatformType.LINUX) { + appendLinuxPrefix(list, 0); + } + } + + + private void appendWin11Prefix(final AutoDetectionMidiPortNamesList list, final int index) { + final String prefix = index > 1 ? "%d- ".formatted(index) : ""; + list.add( + new String[] {"%s%s DAW".formatted(prefix, PORT_NAME), "%s%s MIDI".formatted(prefix, PORT_NAME)}, + new String[] {"%s%s DAW".formatted(prefix, PORT_NAME), "%s%s MIDI".formatted(prefix, PORT_NAME)}); + } + + private void appendLinuxPrefix(final AutoDetectionMidiPortNamesList list, final int index) { + final String prefix = "%s ".formatted(PORT_NAME); + list.add( + new String[] {"%s%s DAW".formatted(prefix, PORT_NAME), "%s%s MIDI".formatted(prefix, PORT_NAME)}, + new String[] {"%s%s DAW".formatted(prefix, PORT_NAME), "%s%s MIDI".formatted(prefix, PORT_NAME)}); + } + + @Override + public MiniLab3Extension createInstance(final ControllerHost host) { + return new MiniLab3Extension(this, host, MinilabModel.MINILAB_37); + } +} diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab3Extension.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab3Extension.java index 37211d55..c7e5dde0 100644 --- a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab3Extension.java +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab3Extension.java @@ -36,6 +36,7 @@ import com.bitwig.extension.controller.api.RelativeHardwareValueMatcher; import com.bitwig.extension.controller.api.RemoteControl; import com.bitwig.extension.controller.api.Scene; +import com.bitwig.extension.controller.api.SceneBank; import com.bitwig.extension.controller.api.SettableEnumValue; import com.bitwig.extension.controller.api.SettableRangedValue; import com.bitwig.extension.controller.api.StringValue; @@ -48,15 +49,14 @@ import com.bitwig.extensions.framework.values.ValueObject; public class MiniLab3Extension extends ControllerExtension { - - public static final int NUM_PADS_TRACK = 8; - + private static final int NUM_SLIDERS = 4; private static final int[] SLIDER_CC_MAPPING = new int[] {0x0E, 0x0F, 0x1E, 0x1F}; private static final int[] ENCODER_CC_MAPPING = new int[] {0x56, 0x57, 0x59, 0x5A, 0x6E, 0x6F, 0x74, 0x75}; - + private static final String ANALOG_LAB_V_DEVICE_ID = "4172747541564953416C617650726F63"; - + + private final MinilabModel model; private Layers layers; private MidiIn midiIn; private MidiOut midiOut; @@ -64,28 +64,28 @@ public class MiniLab3Extension extends ControllerExtension { private HardwareSurface surface; private ControllerHost host; private OledDisplay oled; - + private final AbsoluteHardwareKnob[] knobs = new AbsoluteHardwareKnob[8]; private final HardwareSlider[] sliders = new HardwareSlider[NUM_SLIDERS]; - private final RgbButton[] padBankAButtons = new RgbButton[NUM_PADS_TRACK]; - private final RgbButton[] padBankBButtons = new RgbButton[NUM_PADS_TRACK]; - + private final MinilabRgbButton[] padBankAButtons = new MinilabRgbButton[8]; + private final MinilabRgbButton[] padBankBButtons = new MinilabRgbButton[8]; + private HardwareButton shiftButton; private int blinkState = 0; private CursorTrack cursorTrack; - + private PinnableCursorDevice cursorDevice; private CursorRemoteControlsPage parameterBank; - + private ClipLaunchingLayer clipLaunchingLayer; private DrumPadLayer drumPadLayer; private ArturiaModeLayer arturiaModeLayer; - + private Scene sceneTrackItem; - + private final ValueObject padBank = new ValueObject<>(PadBank.BANK_A); private SysExHandler sysExHandler; - + private TrackBank viewTrackBank; private Runnable nextPingAction = null; private BrowserLayer browserLayer; @@ -98,19 +98,22 @@ public class MiniLab3Extension extends ControllerExtension { private PinnableCursorDevice primaryDevice; private FocusMode recordFocusMode = FocusMode.ARRANGER; private final EncoderStateMaschine encoderStateMaschine = new EncoderStateMaschine(); - - protected MiniLab3Extension(final MiniLab3ExtensionDefinition definition, final ControllerHost host) { + private SceneBank sceneBank; + + protected MiniLab3Extension(final MiniLabExtensionDefinition definition, final ControllerHost host, + final MinilabModel model) { super(definition, host); + this.model = model; } - + private static ControllerHost debugHost; - + public static void println(final String format, final Object... args) { if (debugHost != null) { debugHost.println(format.formatted(args)); } } - + @Override public void init() { host = getHost(); @@ -124,73 +127,61 @@ public void init() { surface = host.createHardwareSurface(); oled = new OledDisplay(sysExHandler, host); transport = host.createTransport(); - final String[] inputMasks = getInputMask(0x09, new int[] { - 0x01, - 0x09, - 0x10, - 0x11, - 0x12, - 0x13, - 0x40, - 0x47, - 0x4a, - 0x4c, - 0x4d, - 0x52, - 0x53, - 0x55, - 0x5d, - 0x70, - 0x71, - 0x72, - 0x73 - }); - final NoteInput noteInput = midiIn.createNoteInput("MIDI", inputMasks); // - - noteInput.setShouldConsumeEvents(true); + final NoteInput noteInput; + if (model == MinilabModel.MINILAB_3) { + final String[] inputMasks = getInputMask( + 0x09, new int[] { + 0x01, 0x09, 0x10, 0x11, 0x12, 0x13, 0x40, 0x47, 0x4a, 0x4c, 0x4d, 0x52, 0x53, 0x55, 0x5d, 0x70, + 0x71, 0x72, 0x73 + }); + noteInput = midiIn.createNoteInput("MIDI", inputMasks); + noteInput.setShouldConsumeEvents(true); + } else { + final MidiIn midiIn2 = host.getMidiInPort(1); + noteInput = midiIn2.createNoteInput("MIDI", "??????"); + noteInput.setShouldConsumeEvents(true); + } initCursors(); setUpHardware(); - + mainLayer = new Layer(layers, "MAIN"); browserLayer = new BrowserLayer(this); - + bindSliderValue(mainLayer, cursorTrack.volume(), sliders[0], cursorTrack.name(), new BasicStringValue("Vol")); - bindKnobValue(4, mainLayer, cursorTrack.pan(), sliders[3], cursorTrack.name(), new BasicStringValue("Pan"), - "Fader" - ); - bindKnobValue(2, mainLayer, cursorTrack.sendBank().getItemAt(0), sliders[1], cursorTrack.name(), - cursorTrack.sendBank().getItemAt(0).name(), "Fader" - ); - bindKnobValue(3, mainLayer, cursorTrack.sendBank().getItemAt(1), sliders[2], cursorTrack.name(), - cursorTrack.sendBank().getItemAt(1).name(), "Fader" - ); - + bindKnobValue( + 4, mainLayer, cursorTrack.pan(), sliders[3], cursorTrack.name(), new BasicStringValue("Pan"), + "Fader"); + bindKnobValue( + 2, mainLayer, cursorTrack.sendBank().getItemAt(0), sliders[1], cursorTrack.name(), + cursorTrack.sendBank().getItemAt(0).name(), "Fader"); + bindKnobValue( + 3, mainLayer, cursorTrack.sendBank().getItemAt(1), sliders[2], cursorTrack.name(), + cursorTrack.sendBank().getItemAt(1).name(), "Fader"); + shiftButton.isPressed().addValueObserver(this::handleShift); - - for (int i = 0; i < NUM_PADS_TRACK; i++) { + + for (int i = 0; i < 8; i++) { final RemoteControl parameter = parameterBank.getParameter(i); bindKnobValue(i + 1, mainLayer, parameter, knobs[i], cursorDevice.name(), parameter.name(), "Knob"); } - + setUpTransportControl(); - + initEncoders(); - + clipLaunchingLayer = new ClipLaunchingLayer(this); drumPadLayer = new DrumPadLayer(this); arturiaModeLayer = new ArturiaModeLayer(this); - + mainLayer.activate(); clipLaunchingLayer.activate(); drumPadLayer.activate(); - sysExHandler.deviceInquiry(); setUpPreferences(); host.scheduleTask(this::handlePing, 100); } - + private void handleSysExData(final String sysEx) { - //MiniLab3Extension.println("<%s>", sysEx); switch (sysEx) { case "f000206b7f420200406300f7": MiniLab3Extension.println(" ==> BANK A"); @@ -216,13 +207,13 @@ private void handleSysExData(final String sysEx) { drumPadLayer.deactivate(); clipLaunchingLayer.deactivate(); arturiaModeLayer.activate(); - host.showPopupNotification("MiniLab 3 Initialized"); + //host.showPopupNotification("MiniLab 3 Initialized"); break; case "f000206b7f420200400101f7": // Confirm Connected to BW Studio sysExHandler.enableProcessing(); oled.notifyInit(); arturiaModeLayer.resetNotes(); - host.showPopupNotification("MiniLab 3 Initialized"); + //host.showPopupNotification("MiniLab 3 Initialized"); break; default: if (sysEx.startsWith("f07e7f060200206b0200040")) { @@ -234,7 +225,7 @@ private void handleSysExData(final String sysEx) { break; } } - + private void handlePing() { blinkState++; clipLaunchingLayer.notifyBlink(blinkState); @@ -245,7 +236,7 @@ private void handlePing() { } host.scheduleTask(this::handlePing, 100); } - + private String[] getInputMask(final int excludeChannel, final int[] miniLabPassThroughCcs) { final List masks = new ArrayList<>(); for (int i = 0; i < 16; i++) { @@ -264,101 +255,116 @@ private String[] getInputMask(final int excludeChannel, final int[] miniLabPassT } return masks.toArray(String[]::new); } - - + + public Transport getTransport() { + return transport; + } + private void initCursors() { - cursorTrack = host.createCursorTrack(2, NUM_PADS_TRACK); - viewTrackBank = host.createTrackBank(NUM_PADS_TRACK, 2, 1); + final int noOfTracks = model == MinilabModel.MINILAB_3 ? 8 : 4; + final int noOfScenes = model == MinilabModel.MINILAB_3 ? 1 : 2; + cursorTrack = host.createCursorTrack(2, noOfTracks); + viewTrackBank = host.createTrackBank(noOfTracks, 2, noOfScenes); + sceneBank = createSceneBank(); + sceneBank.setIndication(true); viewTrackBank.followCursorTrack(cursorTrack); - sceneTrackItem = viewTrackBank.sceneBank().getScene(0); - sceneTrackItem.name() - .addValueObserver( - sceneName -> oled.sendTextInfo(DisplayMode.SCENE, cursorTrack.name().get(), sceneName, true)); - + sceneTrackItem = sceneBank.getScene(0); + sceneTrackItem.name().addValueObserver( + sceneName -> oled.sendTextInfo(DisplayMode.SCENE, cursorTrack.name().get(), sceneName, true)); + cursorDevice = cursorTrack.createCursorDevice(); cursorDevice.hasNext().markInterested(); cursorDevice.hasPrevious().markInterested(); cursorDevice.exists().markInterested(); - - primaryDevice = cursorTrack.createCursorDevice("DrumDetection", "Pad Device", NUM_PADS_TRACK, - CursorDeviceFollowMode.FIRST_INSTRUMENT - ); - + + primaryDevice = + cursorTrack.createCursorDevice("DrumDetection", "Pad Device", 8, CursorDeviceFollowMode.FIRST_INSTRUMENT); + setUpFollowArturiaDevice(); - - + + cursorDevice.presetName().markInterested(); - cursorTrack.name() - .addValueObserver( - name -> updateTrackInfo(transport.isPlaying().get(), transport.isArrangerRecordEnabled().get(), name, - cursorDevice.name().get(), cursorDevice.exists().get() - )); + cursorTrack.name().addValueObserver(name -> updateTrackInfo( + transport.isPlaying().get(), transport.isArrangerRecordEnabled().get(), name, cursorDevice.name().get(), + cursorDevice.exists().get())); cursorDevice.name() - .addValueObserver( - deviceName -> updateTrackInfo(transport.isPlaying().get(), transport.isArrangerRecordEnabled().get(), - cursorTrack.name().get(), deviceName, cursorDevice.exists().get() - )); - cursorDevice.exists() - .addValueObserver( - deviceExists -> updateTrackInfo(transport.isPlaying().get(), transport.isArrangerRecordEnabled().get(), - cursorTrack.name().get(), cursorDevice.name().get(), deviceExists - )); - parameterBank = cursorDevice.createCursorRemoteControlsPage(NUM_PADS_TRACK); + .addValueObserver(deviceName -> updateTrackInfo( + transport.isPlaying().get(), transport.isArrangerRecordEnabled().get(), cursorTrack.name().get(), + deviceName, cursorDevice.exists().get())); + cursorDevice.exists().addValueObserver(deviceExists -> updateTrackInfo( + transport.isPlaying().get(), transport.isArrangerRecordEnabled().get(), cursorTrack.name().get(), + cursorDevice.name().get(), deviceExists)); + parameterBank = cursorDevice.createCursorRemoteControlsPage(8); + } + + private SceneBank createSceneBank() { + if (model == MinilabModel.MINILAB_3) { + return viewTrackBank.sceneBank(); + } else { + final TrackBank singleViewTrackBank = host.createTrackBank(1, 1, 1); + + final SceneBank singleSceneBank = singleViewTrackBank.sceneBank(); + singleSceneBank.scrollPosition() + .addValueObserver(pos -> viewTrackBank.sceneBank().scrollPosition().set(pos)); + return singleSceneBank; + } } - + private void setUpFollowArturiaDevice() { final DeviceMatcher arturiaMatcher = host.createVST3DeviceMatcher(ANALOG_LAB_V_DEVICE_ID); final DeviceBank matcherBank = cursorTrack.createDeviceBank(1); matcherBank.setDeviceMatcher(arturiaMatcher); final Device matcherDevice = matcherBank.getItemAt(0); matcherDevice.exists().markInterested(); - + final BooleanValueObject controlsAnalogLab = new BooleanValueObject(); - - controlsAnalogLab.addValueObserver(controlsLab -> sysExHandler.fireArturiaMode( - controlsLab ? SysExHandler.GeneralMode.ANALOG_LAB : SysExHandler.GeneralMode.DAW_MODE, - arturiaModeLayer.isActive() - )); - + + controlsAnalogLab.addValueObserver( + controlsLab -> sysExHandler.fireArturiaMode( + controlsLab ? SysExHandler.GeneralMode.ANALOG_LAB : SysExHandler.GeneralMode.DAW_MODE, + arturiaModeLayer.isActive())); + final BooleanValue onArturiaDevice = cursorDevice.createEqualsValue(matcherDevice); - cursorTrack.arm() - .addValueObserver( - armed -> controlsAnalogLab.set(armed && cursorDevice.exists().get() && onArturiaDevice.get())); + cursorTrack.arm().addValueObserver( + armed -> controlsAnalogLab.set(armed && cursorDevice.exists().get() && onArturiaDevice.get())); onArturiaDevice.addValueObserver( onArturia -> controlsAnalogLab.set(cursorTrack.arm().get() && cursorDevice.exists().get() && onArturia)); - cursorDevice.exists() - .addValueObserver(cursorDeviceExists -> controlsAnalogLab.set( - cursorTrack.arm().get() && cursorDeviceExists && onArturiaDevice.get())); + cursorDevice.exists().addValueObserver(cursorDeviceExists -> controlsAnalogLab.set( + cursorTrack.arm().get() && cursorDeviceExists && onArturiaDevice.get())); } - - + + private void setUpPreferences() { final DocumentState documentState = getHost().getDocumentState(); // THIS - final SettableEnumValue recordButtonAssignment = documentState.getEnumSetting("Record Button assignment", // + final SettableEnumValue recordButtonAssignment = documentState.getEnumSetting( + "Record Button assignment", // "Transport", new String[] {FocusMode.LAUNCHER.getDescriptor(), FocusMode.ARRANGER.getDescriptor()}, - recordFocusMode.getDescriptor() - ); + recordFocusMode.getDescriptor()); recordButtonAssignment.addValueObserver(value -> { recordFocusMode = FocusMode.toMode(value); updateTrackInfo(); }); final Preferences preferences = getHost().getPreferences(); - final SettableEnumValue clipStopTiming = preferences.getEnumSetting("Long press to stop clip", // - "Clip", new String[] {"Fast", "Medium", "Standard"}, "Medium" - ); + final SettableEnumValue clipStopTiming = preferences.getEnumSetting( + "Long press to stop clip", // + "Clip", new String[] {"Fast", "Medium", "Standard"}, "Medium"); clipStopTiming.addValueObserver(clipLaunchingLayer::setClipStopTiming); } - + void bindEncoder(final Layer layer, final RelativeHardwareKnob encoder, final IntConsumer action) { final HardwareActionBindable incAction = host.createAction(() -> action.accept(1), () -> "+"); final HardwareActionBindable decAction = host.createAction(() -> action.accept(-1), () -> "-"); layer.bind(encoder, host.createRelativeHardwareControlStepTarget(incAction, decAction)); } - + + public MinilabModel getModel() { + return model; + } + private void initEncoders() { bindEncoder(mainLayer, mainEncoder, this::mainEncoderAction); bindEncoder(mainLayer, shiftMainEncoder, this::mainEncoderShiftAction); - + parameterBank.pageNames().addValueObserver(pages -> { pageNames = pages; showParameterPage(parameterBank.selectedPageIndex().get()); @@ -368,15 +374,15 @@ private void initEncoders() { shiftEncoderPress.isPressed().addValueObserver(this::handleShiftEncoderPressed); encoderPress.isPressed().addValueObserver(this::handleEncoderPressed); } - + public RelativeHardwareKnob getMainEncoder() { return mainEncoder; } - + public RelativeHardwareKnob getShiftMainEncoder() { return shiftMainEncoder; } - + private void showParameterPage(final int index) { if (parameterBank.pageCount().get() == 0) { oled.sendTextInfo(DisplayMode.PARAM_PAGE, cursorDevice.name().get(), "", true); @@ -384,46 +390,44 @@ private void showParameterPage(final int index) { oled.sendTextInfo(DisplayMode.PARAM_PAGE, cursorDevice.name().get(), pageNames[index], true); } } - + public OledDisplay getOled() { return oled; } - + public SysExHandler getSysExHandler() { return sysExHandler; } - + public Layers getLayers() { return layers; } - + public PinnableCursorDevice getCursorDevice() { return cursorDevice; } - + public PinnableCursorDevice getPrimaryDevice() { return primaryDevice; } - + private void bindSliderValue(final Layer layer, final Parameter parameter, final AbsoluteHardwareControl slider, final StringValue nameSource, final StringValue label) { layer.bind(slider, parameter.value()); layer.bind(slider, v -> oled.enableValues(DisplayMode.PARAM)); label.addValueObserver( - v -> oled.sendSliderInfo(DisplayMode.PARAM, parameter.value().get(), v + " : " + nameSource.get(), - parameter.value().displayedValue().get() - )); - parameter.value() - .displayedValue() - .addValueObserver(displayedValue -> oled.sendSliderInfo(DisplayMode.PARAM, parameter.value().get(), - label.get() + " : " + nameSource.get(), displayedValue - )); - parameter.value() - .addValueObserver(v -> oled.sendSliderInfo(DisplayMode.PARAM, v, label.get() + " : " + nameSource.get(), - parameter.value().displayedValue().get() - )); - } - + v -> oled.sendSliderInfo( + DisplayMode.PARAM, parameter.value().get(), v + " : " + nameSource.get(), + parameter.value().displayedValue().get())); + parameter.value().displayedValue().addValueObserver( + displayedValue -> oled.sendSliderInfo( + DisplayMode.PARAM, parameter.value().get(), label.get() + " : " + nameSource.get(), displayedValue)); + parameter.value().addValueObserver( + v -> oled.sendSliderInfo( + DisplayMode.PARAM, v, label.get() + " : " + nameSource.get(), + parameter.value().displayedValue().get())); + } + private void bindKnobValue(final int index, final Layer layer, final Parameter parameter, final AbsoluteHardwareControl slider, final StringValue nameSource, final StringValue label, final String type) { @@ -435,89 +439,98 @@ private void bindKnobValue(final int index, final Layer layer, final Parameter p layer.bind(slider, v -> oled.enableValues(DisplayMode.PARAM)); value.displayedValue().markInterested(); label.addValueObserver( - v -> oled.sendSliderInfo(DisplayMode.PARAM, value.get(), String.format("%s : %s", v, nameSource.get()), - value.displayedValue().get() - )); - value.displayedValue() - .addValueObserver(displayedValue -> oled.sendEncoderInfo(DisplayMode.PARAM, value.get(), - String.format("%s : %s", label.get(), nameSource.get()), displayedValue - )); + v -> oled.sendSliderInfo( + DisplayMode.PARAM, value.get(), String.format("%s : %s", v, nameSource.get()), + value.displayedValue().get())); + value.displayedValue().addValueObserver(displayedValue -> oled.sendEncoderInfo( + DisplayMode.PARAM, value.get(), + String.format("%s : %s", label.get(), nameSource.get()), displayedValue)); value.addValueObserver( - v -> oled.sendEncoderInfo(DisplayMode.PARAM, v, String.format("%s : %s", label.get(), nameSource.get()), - value.displayedValue().get() - )); + v -> oled.sendEncoderInfo( + DisplayMode.PARAM, v, String.format("%s : %s", label.get(), nameSource.get()), + value.displayedValue().get())); if (type.equals("Fader")) { slider.value().addValueObserver(v -> { if (!parameter.exists().get()) { - oled.sendSliderInfo(DisplayMode.PARAM, v, String.format("%s : %d", type, index), - String.format("%d", (int) Math.round(v * 127)) - ); + oled.sendSliderInfo( + DisplayMode.PARAM, v, String.format("%s : %d", type, index), + String.format("%d", (int) Math.round(v * 127))); } }); } else { slider.value().addValueObserver(v -> { if (!parameter.exists().get()) { - oled.sendEncoderInfo(DisplayMode.PARAM, v, String.format("%s : %d", type, index), - String.format("%d", (int) Math.round(v * 127)) - ); + oled.sendEncoderInfo( + DisplayMode.PARAM, v, String.format("%s : %d", type, index), + String.format("%d", (int) Math.round(v * 127))); } }); } } - + private void setUpTransportControl() { - final RgbButton loopButton = new RgbButton(0x57, PadBank.TRANSPORT, RgbButton.Type.CC, 105, 0, true, this); - loopButton.bindToggle(mainLayer, transport.isArrangerLoopEnabled(), RgbLightState.ORANGE, - RgbLightState.ORANGE_DIMMED - ); - transport.isArrangerLoopEnabled() - .addValueObserver( - loopEnabled -> oled.sendTextCond(DisplayMode.LOOP_VALUE, "Loop Mode", loopEnabled ? "ON" : "OFF")); - loopButton.bind(mainLayer, () -> { - final boolean loopEnabled = transport.isArrangerLoopEnabled().get(); - oled.sendText(DisplayMode.LOOP_VALUE, "Loop Mode", loopEnabled ? "ON" : "OFF"); - }, () -> transport.isArrangerLoopEnabled().get() ? RgbLightState.ORANGE : RgbLightState.ORANGE_DIMMED); - - final RgbButton recordButton = new RgbButton(0x5A, PadBank.TRANSPORT, RgbButton.Type.CC, 108, 0, true, this); + final MinilabRgbButton loopButton = + new MinilabRgbButton(0x57, PadBank.TRANSPORT, MinilabRgbButton.Type.CC, 105, 0, this); + loopButton.bindToggle( + mainLayer, transport.isArrangerLoopEnabled(), RgbLightState.ORANGE, + RgbLightState.ORANGE_DIMMED); + transport.isArrangerLoopEnabled().addValueObserver( + loopEnabled -> oled.sendTextCond(DisplayMode.LOOP_VALUE, "Loop Mode", loopEnabled ? "ON" : "OFF")); + loopButton.bind( + mainLayer, () -> { + final boolean loopEnabled = transport.isArrangerLoopEnabled().get(); + oled.sendText(DisplayMode.LOOP_VALUE, "Loop Mode", loopEnabled ? "ON" : "OFF"); + }, () -> transport.isArrangerLoopEnabled().get() ? RgbLightState.ORANGE : RgbLightState.ORANGE_DIMMED); + + final MinilabRgbButton recordButton = + new MinilabRgbButton(0x5A, PadBank.TRANSPORT, MinilabRgbButton.Type.CC, 108, 0, this); transport.isArrangerRecordEnabled().markInterested(); transport.isClipLauncherOverdubEnabled().markInterested(); - + recordButton.bind(mainLayer, this::handleRecordPressed, this::getRecordingLightState); - - final RgbButton playButton = new RgbButton(0x59, PadBank.TRANSPORT, RgbButton.Type.CC, 107, 0, true, this); + + final MinilabRgbButton playButton = + new MinilabRgbButton(0x59, PadBank.TRANSPORT, MinilabRgbButton.Type.CC, 107, 0, this); playButton.bindToggle(mainLayer, transport.isPlaying(), RgbLightState.GREEN, RgbLightState.GREEN_DIMMED); - - final RgbButton stopButton = new RgbButton(0x58, PadBank.TRANSPORT, RgbButton.Type.CC, 106, 0, true, this); - stopButton.bindPressed(mainLayer, pressed -> { - if (pressed) { - transport.stop(); - } - }, () -> transport.isPlaying().get() ? RgbLightState.WHITE : RgbLightState.WHITE_DIMMED); - - final RgbButton tapButton = new RgbButton(0x5B, PadBank.TRANSPORT, RgbButton.Type.CC, 109, 0, true, this); + + final MinilabRgbButton stopButton = + new MinilabRgbButton(0x58, PadBank.TRANSPORT, MinilabRgbButton.Type.CC, 106, 0, this); + stopButton.bindPressed(mainLayer, this::handleStopPressed); + stopButton.bindLightState( + mainLayer, () -> transport.isPlaying().get() ? RgbLightState.WHITE : RgbLightState.WHITE_DIMMED); + + final MinilabRgbButton tapButton = + new MinilabRgbButton(0x5B, PadBank.TRANSPORT, MinilabRgbButton.Type.CC, 109, 0, this); transport.tempo().value().addRawValueObserver(v -> { final int tempo = (int) Math.round(v); oled.sendTextCond(DisplayMode.TEMPO, "Tap Tempo", String.format("%d BPM", tempo)); }); - tapButton.bind(mainLayer, () -> { - transport.tapTempo(); - final int tempo = (int) Math.round(transport.tempo().value().getRaw()); - oled.sendText(DisplayMode.TEMPO, "Tap Tempo", String.format("%d BPM", tempo)); - }, RgbLightState.WHITE, RgbLightState.WHITE_DIMMED); - + tapButton.bind( + mainLayer, () -> { + transport.tapTempo(); + final int tempo = (int) Math.round(transport.tempo().value().getRaw()); + oled.sendText(DisplayMode.TEMPO, "Tap Tempo", String.format("%d BPM", tempo)); + }, RgbLightState.WHITE, RgbLightState.WHITE_DIMMED); + transport.isArrangerRecordEnabled() - .addValueObserver(isRecording -> updateTrackInfo(transport.isPlaying().get(), - recordFocusMode == FocusMode.ARRANGER ? isRecording : recordFocusMode.getState(transport) - )); + .addValueObserver(isRecording -> updateTrackInfo( + transport.isPlaying().get(), + recordFocusMode == FocusMode.ARRANGER ? isRecording : recordFocusMode.getState(transport))); transport.isClipLauncherOverdubEnabled() - .addValueObserver(isRecording -> updateTrackInfo(transport.isPlaying().get(), - recordFocusMode == FocusMode.LAUNCHER ? isRecording : recordFocusMode.getState(transport) - )); - + .addValueObserver(isRecording -> updateTrackInfo( + transport.isPlaying().get(), + recordFocusMode == FocusMode.LAUNCHER ? isRecording : recordFocusMode.getState(transport))); + transport.isPlaying() .addValueObserver(isPlaying -> updateTrackInfo(isPlaying, recordFocusMode.getState(transport))); } - + + private void handleStopPressed(final Boolean pressed) { + if (pressed) { + transport.stop(); + } + } + private RgbLightState getRecordingLightState() { if (recordFocusMode == FocusMode.ARRANGER) { return transport.isArrangerRecordEnabled().get() ? RgbLightState.RED : RgbLightState.RED_DIMMED; @@ -525,7 +538,7 @@ private RgbLightState getRecordingLightState() { return transport.isClipLauncherOverdubEnabled().get() ? RgbLightState.RED : RgbLightState.RED_DIMMED; } } - + private void handleRecordPressed() { if (recordFocusMode == FocusMode.ARRANGER) { transport.isArrangerRecordEnabled().toggle(); @@ -533,44 +546,44 @@ private void handleRecordPressed() { transport.isClipLauncherOverdubEnabled().toggle(); } } - + public void browserDisplayMode(final boolean browserModeActive) { - oled.setMainMode(browserModeActive ? DisplayMode.BROWSER : DisplayMode.TRACK, - browserModeActive ? browserLayer::updateInfo : this::updateTrackInfo - ); + oled.setMainMode( + browserModeActive ? DisplayMode.BROWSER : DisplayMode.TRACK, + browserModeActive ? browserLayer::updateInfo : this::updateTrackInfo); if (!browserModeActive) { updateTrackInfo(); } } - + private void updateTrackInfo(final boolean isPlaying, final boolean isRecording, final String trackName, final String deviceName, final boolean deviceExists) { - oled.sendPictogramInfo(DisplayMode.TRACK, isRecording ? OledDisplay.Pict.REC : OledDisplay.Pict.NONE, + oled.sendPictogramInfo( + DisplayMode.TRACK, isRecording ? OledDisplay.Pict.REC : OledDisplay.Pict.NONE, isPlaying ? OledDisplay.Pict.PLAY : OledDisplay.Pict.NONE, trackName, - deviceExists ? deviceName : "" - ); + deviceExists ? deviceName : ""); } - + private void updateTrackInfo(final boolean playing, final boolean recording) { - updateTrackInfo(playing, recording, cursorTrack.name().get(), cursorDevice.name().get(), - cursorDevice.exists().get() - ); + updateTrackInfo( + playing, recording, cursorTrack.name().get(), cursorDevice.name().get(), + cursorDevice.exists().get()); } - + private void updateTrackInfo() { - updateTrackInfo(transport.isPlaying().get(), recordFocusMode.getState(transport), cursorTrack.name().get(), - cursorDevice.name().get(), cursorDevice.exists().get() - ); + updateTrackInfo( + transport.isPlaying().get(), recordFocusMode.getState(transport), cursorTrack.name().get(), + cursorDevice.name().get(), cursorDevice.exists().get()); } - + public ValueObject getPadBank() { return padBank; } - + private void toBankMode(final PadBank bankMode) { padBank.set(bankMode); } - + private RelativeHardwareKnob createMainEncoder(final int ccNr) { final RelativeHardwareKnob mainEncoder = surface.createRelativeHardwareKnob("MAIN_ENCODER+_" + ccNr); final RelativeHardwareValueMatcher stepUpMatcher = @@ -583,24 +596,24 @@ private RelativeHardwareKnob createMainEncoder(final int ccNr) { mainEncoder.setStepSize(1); return mainEncoder; } - + private HardwareButton createEncoderPress(final int ccNr, final String name) { final HardwareButton encoderButton = surface.createHardwareButton(name); encoderButton.pressedAction().setActionMatcher(midiIn.createCCActionMatcher(0, ccNr, 127)); encoderButton.releasedAction().setActionMatcher(midiIn.createCCActionMatcher(0, ccNr, 0)); return encoderButton; } - + private void handleShift(final boolean pressed) { encoderStateMaschine.doTransition( pressed ? EncoderStateMaschine.Event.SHIFT_DOWN : EncoderStateMaschine.Event.SHIFT_UP); } - + /** * when in mode HOLD+SHIFT and you release the Hold button, you of stay in the parameter/device mode. * from now on, */ - + private void handleEncoderPressed(final boolean down) { if (browserLayer.isActive()) { browserLayer.pressAction(down); @@ -621,7 +634,7 @@ private void handleEncoderPressed(final boolean down) { down ? EncoderStateMaschine.Event.ENCODER_DOWN : EncoderStateMaschine.Event.ENCODER_UP); } } - + private void handleShiftEncoderPressed(final boolean down) { if (!down && encoderStateMaschine.getState() == EncoderStateMaschine.State.SHIFT_HOLD && !encoderStateMaschine.isTurnAction()) { @@ -634,7 +647,7 @@ private void handleShiftEncoderPressed(final boolean down) { encoderStateMaschine.doTransition( down ? EncoderStateMaschine.Event.ENCODER_DOWN : EncoderStateMaschine.Event.ENCODER_UP); } - + private void mainEncoderAction(final int dir) { encoderStateMaschine.notifyTurn(false); oled.disableValues(); @@ -643,7 +656,7 @@ private void mainEncoderAction(final int dir) { case HOLD -> navigateParametersBanks(dir); } } - + private void mainEncoderShiftAction(final int dir) { encoderStateMaschine.notifyTurn(true); oled.disableValues(); @@ -652,17 +665,17 @@ private void mainEncoderShiftAction(final int dir) { case SHIFT -> navigateTracks(dir); } } - + private void navigateScenesOrPads(final int dir) { if (padBank.get() == PadBank.BANK_A) { oled.enableValues(DisplayMode.SCENE); oled.sendTextInfo(DisplayMode.SCENE, cursorTrack.name().get(), sceneTrackItem.name().get(), true); - clipLaunchingLayer.navigateScenes(dir); + navigateScenes(dir); } else { drumPadLayer.navigate(dir); } } - + private void navigateDevice(final int dir) { if (!cursorDevice.exists().get() && !cursorDevice.hasNext().get() && !cursorDevice.hasPrevious().get()) { cursorDevice.selectFirstInChannel(cursorTrack); @@ -672,7 +685,7 @@ private void navigateDevice(final int dir) { cursorDevice.selectPrevious(); } } - + private void navigateTracks(final int dir) { if (dir > 0) { cursorTrack.selectNext(); @@ -680,7 +693,11 @@ private void navigateTracks(final int dir) { cursorTrack.selectPrevious(); } } - + + public void navigateScenes(final int direction) { + sceneBank.scrollBy(direction); + } + private void navigateParametersBanks(final int dir) { oled.enableValues(DisplayMode.PARAM_PAGE); showParameterPage(parameterBank.selectedPageIndex().get()); @@ -690,21 +707,23 @@ private void navigateParametersBanks(final int dir) { parameterBank.selectPrevious(); } } - + private void setUpHardware() { mainEncoder = createMainEncoder(0x1C); shiftMainEncoder = createMainEncoder(0x1D); encoderPress = createEncoderPress(0x76, "ENCODER_PRESSED"); shiftEncoderPress = createEncoderPress(0x77, "SHIFT_ENCODER_PRESSED"); - + for (int i = 0; i < ENCODER_CC_MAPPING.length; i++) { knobs[i] = surface.createAbsoluteHardwareKnob("KNOB_" + (i + 1)); knobs[i].setAdjustValueMatcher(midiIn.createAbsoluteCCValueMatcher(0, ENCODER_CC_MAPPING[i])); - - padBankAButtons[i] = new RgbButton(i + 0x34, PadBank.BANK_A, RgbButton.Type.NOTE, 0x24 + i, 9, false, this); - padBankBButtons[i] = new RgbButton(i + 0x44, PadBank.BANK_B, RgbButton.Type.NOTE, 0x2C + i, 9, false, this); + final int offsetIndex = model == MinilabModel.MINILAB_3 ? i : (1 - i / 4) * 4 + i % 4; + padBankAButtons[i] = + new MinilabRgbButton(i + 0x34, PadBank.BANK_A, MinilabRgbButton.Type.NOTE, 0x24 + offsetIndex, 9, this); + padBankBButtons[i] = + new MinilabRgbButton(i + 0x44, PadBank.BANK_B, MinilabRgbButton.Type.NOTE, 0x2C + offsetIndex, 9, this); } - + for (int i = 0; i < SLIDER_CC_MAPPING.length; i++) { sliders[i] = surface.createHardwareSlider("FADER_" + (i + 1)); sliders[i].setAdjustValueMatcher(midiIn.createAbsoluteCCValueMatcher(0, SLIDER_CC_MAPPING[i])); @@ -713,29 +732,29 @@ private void setUpHardware() { shiftButton.pressedAction().setActionMatcher(midiIn.createCCActionMatcher(0, 27, 127)); shiftButton.releasedAction().setActionMatcher(midiIn.createCCActionMatcher(0, 27, 0)); } - - public RgbButton[] getPadBankAButtons() { + + public MinilabRgbButton[] getPadBankAButtons() { return padBankAButtons; } - - public RgbButton[] getPadBankBButtons() { + + public MinilabRgbButton[] getPadBankBButtons() { return padBankBButtons; } - + public CursorTrack getCursorTrack() { return cursorTrack; } - + public TrackBank getViewTrackBank() { return viewTrackBank; } - + public void updateBankState(final InternalHardwareLightState state) { if (state instanceof final RgbBankLightState lightState) { sysExHandler.sendBankState(lightState); } } - + private void onMidi0(final ShortMidiMessage msg) { final int channel = msg.getChannel(); final int sb = msg.getStatusByte() & (byte) 0xF0; @@ -743,19 +762,19 @@ private void onMidi0(final ShortMidiMessage msg) { drumPadLayer.notifyNote(sb, msg.getData1()); } } - + public HardwareSurface getSurface() { return surface; } - + public MidiIn getMidiIn() { return midiIn; } - + public MidiOut getMidiOut() { return midiOut; } - + @Override public void exit() { final CompletableFuture shutdown = new CompletableFuture<>(); @@ -777,18 +796,18 @@ public void exit() { e.printStackTrace(); } } - + @Override public void flush() { surface.updateHardware(); } - + /** * Make sure no scene is launched upon release. */ public void notifyTurn(final boolean shift) { encoderStateMaschine.notifyTurn(shift); } - - + + } diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab3ExtensionDefinition.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab3ExtensionDefinition.java index 79d80bdf..b802f2c5 100644 --- a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab3ExtensionDefinition.java +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLab3ExtensionDefinition.java @@ -1,98 +1,63 @@ package com.bitwig.extensions.controllers.arturia.minilab3; +import java.util.UUID; + import com.bitwig.extension.api.PlatformType; import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList; -import com.bitwig.extension.controller.ControllerExtensionDefinition; import com.bitwig.extension.controller.api.ControllerHost; -import java.util.UUID; - -public class MiniLab3ExtensionDefinition extends ControllerExtensionDefinition { - private static final UUID DRIVER_ID = UUID.fromString("6d0d76fc-b9b0-11ec-8422-0242ac120002"); - - private static final String PORT_NAME_MIDI = "Minilab3 MIDI"; - private static final String PORT_NAME = "Minilab3"; - private static final String PORT_NAME_LINUX = "Minilab3 Minilab3 MIDI"; - - public MiniLab3ExtensionDefinition() { - } - - @Override - public String getHelpFilePath() { - return "Controllers/Arturia/Arturia MiniLab 3.pdf"; - } - - @Override - public String getName() { - return "MiniLab 3"; - } - - @Override - public String getAuthor() { - return "Bitwig"; - } - - @Override - public String getVersion() { - return "1.01"; - } - - @Override - public UUID getId() { - return DRIVER_ID; - } - - @Override - public String getHardwareVendor() { - return "Arturia"; - } - - @Override - public String getHardwareModel() { - return "MiniLab 3"; - } - - @Override - public int getRequiredAPIVersion() { - return 18; - } - - @Override - public int getNumMidiInPorts() { - return 1; - } - - @Override - public int getNumMidiOutPorts() { - return 1; - } - - @Override - public void listAutoDetectionMidiPortNames(final AutoDetectionMidiPortNamesList list, - final PlatformType platformType) { - if (platformType == PlatformType.WINDOWS) { - list.add(new String[]{PORT_NAME_MIDI}, new String[]{PORT_NAME_MIDI}); - list.add(new String[]{PORT_NAME}, new String[]{PORT_NAME}); - appendRenamedPorts(4, list); - } else if (platformType == PlatformType.MAC) { - list.add(new String[]{PORT_NAME_MIDI}, new String[]{PORT_NAME_MIDI}); - } else if (platformType == PlatformType.LINUX) { - list.add(new String[]{PORT_NAME_LINUX}, new String[]{PORT_NAME_LINUX}); - } - } - - private void appendRenamedPorts(int count, AutoDetectionMidiPortNamesList list) { - for (int i = 2; i < count + 2; i++) { - list.add(getRenamedPorts(i), getRenamedPorts(i)); - } - } - - private String[] getRenamedPorts(int index) { - return new String[]{"%d- %s".formatted(index, PORT_NAME_MIDI)}; - } - - @Override - public MiniLab3Extension createInstance(final ControllerHost host) { - return new MiniLab3Extension(this, host); - } +public class MiniLab3ExtensionDefinition extends MiniLabExtensionDefinition { + private static final UUID DRIVER_ID = UUID.fromString("6d0d76fc-b9b0-11ec-8422-0242ac120002"); + + private static final String PORT_NAME_MIDI = "Minilab3 MIDI"; + private static final String PORT_NAME = "Minilab3"; + private static final String PORT_NAME_LINUX = "Minilab3 Minilab3 MIDI"; + + public MiniLab3ExtensionDefinition() { + super(); + } + + @Override + public String getName() { + return "MiniLab 3"; + } + + @Override + public UUID getId() { + return DRIVER_ID; + } + + @Override + public String getHardwareModel() { + return "MiniLab 3"; + } + + @Override + public void listAutoDetectionMidiPortNames(final AutoDetectionMidiPortNamesList list, + final PlatformType platformType) { + if (platformType == PlatformType.WINDOWS) { + list.add(new String[] {PORT_NAME_MIDI}, new String[] {PORT_NAME_MIDI}); + list.add(new String[] {PORT_NAME}, new String[] {PORT_NAME}); + appendRenamedPorts(4, list); + } else if (platformType == PlatformType.MAC) { + list.add(new String[] {PORT_NAME_MIDI}, new String[] {PORT_NAME_MIDI}); + } else if (platformType == PlatformType.LINUX) { + list.add(new String[] {PORT_NAME_LINUX}, new String[] {PORT_NAME_LINUX}); + } + } + + private void appendRenamedPorts(final int count, final AutoDetectionMidiPortNamesList list) { + for (int i = 2; i < count + 2; i++) { + list.add(getRenamedPorts(i), getRenamedPorts(i)); + } + } + + private String[] getRenamedPorts(final int index) { + return new String[] {"%d- %s".formatted(index, PORT_NAME_MIDI)}; + } + + @Override + public MiniLab3Extension createInstance(final ControllerHost host) { + return new MiniLab3Extension(this, host, MinilabModel.MINILAB_3); + } } diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLabExtensionDefinition.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLabExtensionDefinition.java new file mode 100644 index 00000000..da3ae8f0 --- /dev/null +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MiniLabExtensionDefinition.java @@ -0,0 +1,44 @@ +package com.bitwig.extensions.controllers.arturia.minilab3; + +import com.bitwig.extension.controller.ControllerExtensionDefinition; + +public abstract class MiniLabExtensionDefinition extends ControllerExtensionDefinition { + + public MiniLabExtensionDefinition() { + } + + @Override + public String getHelpFilePath() { + return "Controllers/Arturia/Arturia MiniLab 3.pdf"; + } + + @Override + public String getAuthor() { + return "Bitwig"; + } + + @Override + public String getVersion() { + return "1.10"; + } + + @Override + public String getHardwareVendor() { + return "Arturia"; + } + + @Override + public int getRequiredAPIVersion() { + return 22; + } + + @Override + public int getNumMidiInPorts() { + return 1; + } + + @Override + public int getNumMidiOutPorts() { + return 1; + } +} diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MinilabModel.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MinilabModel.java new file mode 100644 index 00000000..0efb70bf --- /dev/null +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MinilabModel.java @@ -0,0 +1,6 @@ +package com.bitwig.extensions.controllers.arturia.minilab3; + +public enum MinilabModel { + MINILAB_3, + MINILAB_37 +} diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MinilabRgbButton.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MinilabRgbButton.java new file mode 100644 index 00000000..7ef1f661 --- /dev/null +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/MinilabRgbButton.java @@ -0,0 +1,119 @@ +package com.bitwig.extensions.controllers.arturia.minilab3; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import com.bitwig.extension.controller.api.HardwareButton; +import com.bitwig.extension.controller.api.InternalHardwareLightState; +import com.bitwig.extension.controller.api.MidiIn; +import com.bitwig.extension.controller.api.MultiStateHardwareLight; +import com.bitwig.extension.controller.api.SettableBooleanValue; +import com.bitwig.extensions.framework.Layer; + +public class MinilabRgbButton { + private final byte[] rgbCommand = { + (byte) 0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, // + 0x02, // 7 - Patch Id + 0x16, // 8 - Command + 0x02, // 9 - Pad ID + 0x00, // 10 - Red + 0x00, // 11 - Green + 0x00, // 12 - blue + (byte) 0xF7 + }; + + private final HardwareButton hwButton; + private final MultiStateHardwareLight light; + private final int padId; + private final PadBank bankId; + private final SysExHandler sysExHandler; + private final int noteValue; + + public enum Type { + NOTE, + CC + } + + public MinilabRgbButton(final int padId, final PadBank bankId, final Type type, final int midiValue, + final int channel, final MiniLab3Extension driver) { + this.padId = padId; + this.bankId = bankId; + noteValue = midiValue; + final MidiIn midiIn = driver.getMidiIn(); + sysExHandler = driver.getSysExHandler(); + rgbCommand[7] = (byte) 0x2; // DAW Preset + rgbCommand[9] = (byte) padId; + hwButton = driver.getSurface().createHardwareButton("RGB_PAD_" + midiValue); + if (type == Type.NOTE) { + hwButton.pressedAction().setActionMatcher(midiIn.createNoteOnActionMatcher(channel, midiValue)); + hwButton.releasedAction().setActionMatcher(midiIn.createNoteOffActionMatcher(channel, midiValue)); + } else { + hwButton.pressedAction().setActionMatcher(midiIn.createCCActionMatcher(channel, midiValue, 127)); + hwButton.releasedAction().setActionMatcher(midiIn.createCCActionMatcher(channel, midiValue, 0)); + } + hwButton.isPressed().markInterested(); + light = driver.getSurface().createMultiStateHardwareLight("RGB_PAD_LIGHT_" + midiValue); + hwButton.setBackgroundLight(light); + if (bankId.getIndex() == -1) { // Individual updates handled + light.state().onUpdateHardware(this::updateState); + } + } + + public Integer getNoteValue() { + return noteValue; + } + + + public MultiStateHardwareLight getLight() { + return light; + } + + private void updateState(final InternalHardwareLightState state) { + if (state instanceof RgbLightState) { + ((RgbLightState) state).apply(rgbCommand); + sysExHandler.sendColor((byte) padId, bankId, rgbCommand[10], rgbCommand[11], rgbCommand[12]); + } else { + setRgbOff(); + } + } + + private void setRgbOff() { + rgbCommand[10] = 0; + rgbCommand[11] = 0; + rgbCommand[12] = 0; + } + + public void bindPressed(final Layer layer, final Consumer target) { + layer.bind(hwButton, hwButton.pressedAction(), () -> target.accept(true)); + layer.bind(hwButton, hwButton.releasedAction(), () -> target.accept(false)); + } + + public void bindLightState(final Layer layer, final Supplier lightSource) { + layer.bindLightState(lightSource::get, light); + } + + public void bind(final Layer layer, final Runnable action, final Supplier lightSource) { + layer.bind(hwButton, hwButton.pressedAction(), action); + layer.bind( + hwButton, hwButton.releasedAction(), () -> { + }); + layer.bindLightState(lightSource::get, light); + } + + public void bind(final Layer layer, final Runnable action, final RgbLightState pressOn, + final RgbLightState pressOff) { + layer.bind(hwButton, hwButton.pressedAction(), action); + layer.bind( + hwButton, hwButton.releasedAction(), () -> { + }); + layer.bindLightState(() -> hwButton.isPressed().get() ? pressOn : pressOff, light); + } + + public void bindToggle(final Layer layer, final SettableBooleanValue value, final RgbLightState onColor, + final RgbLightState offColor) { + value.markInterested(); + layer.bind(hwButton, hwButton.pressedAction(), value::toggle); + layer.bindLightState(() -> value.get() ? onColor : offColor, light); + } + +} diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbBankLightState.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbBankLightState.java index f941eec1..515fee9d 100644 --- a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbBankLightState.java +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbBankLightState.java @@ -6,17 +6,17 @@ import com.bitwig.extension.controller.api.InternalHardwareLightState; public class RgbBankLightState extends InternalHardwareLightState { - + private final byte[] colors; private final PadBank bank; - + public static class Handler { private final byte[] buffer = new byte[24]; - private final RgbButton[] buttons; + private final MinilabRgbButton[] buttons; private final PadBank bank; private RgbBankLightState lastState; - - public Handler(final PadBank bank, final RgbButton[] buttons) { + + public Handler(final PadBank bank, final MinilabRgbButton[] buttons) { this.buttons = buttons; this.bank = bank; final byte[] colors = new byte[24]; @@ -24,7 +24,7 @@ public Handler(final PadBank bank, final RgbButton[] buttons) { System.arraycopy(colors, 0, buffer, 0, 24); lastState = new RgbBankLightState(bank, colors); } - + public InternalHardwareLightState getBankLightState() { calcColors(buffer); if (!lastState.equalsColorData(buffer)) { @@ -32,12 +32,11 @@ public InternalHardwareLightState getBankLightState() { } return lastState; } - + private void calcColors(final byte[] colorArray) { for (int i = 0; i < buttons.length; i++) { final InternalHardwareLightState state = buttons[i].getLight().state().currentValue(); - if (state instanceof RgbLightState) { - final RgbLightState colorState = (RgbLightState) state; + if (state instanceof final RgbLightState colorState) { colorArray[i * 3] = colorState.getRed(); colorArray[i * 3 + 1] = colorState.getGreen(); colorArray[i * 3 + 2] = colorState.getBlue(); @@ -45,30 +44,30 @@ private void calcColors(final byte[] colorArray) { } } } - + RgbBankLightState(final PadBank bank, final byte[] colors) { this.bank = bank; this.colors = new byte[24]; System.arraycopy(colors, 0, this.colors, 0, 24); } - + public PadBank getBank() { return bank; } - + public byte[] getColors() { return colors; } - + boolean equalsColorData(final byte[] colors) { return Arrays.equals(this.colors, colors); } - + @Override public HardwareLightVisualState getVisualState() { return null; } - + @Override public boolean equals(final Object obj) { if (obj instanceof final RgbBankLightState other) { diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbButton.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbButton.java deleted file mode 100644 index 75c2ff3e..00000000 --- a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbButton.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.bitwig.extensions.controllers.arturia.minilab3; - -import com.bitwig.extension.controller.api.*; -import com.bitwig.extensions.framework.Layer; - -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class RgbButton { - private final byte[] rgbCommand = {(byte) 0xF0, 0x00, 0x20, 0x6B, 0x7F, 0x42, 0x02, // - 0x02, // 7 - Patch Id - 0x16, // 8 - Command - 0x02, // 9 - Pad ID - 0x00, // 10 - Red - 0x00, // 11 - Green - 0x00, // 12 - blue - (byte) 0xF7}; - - private final HardwareButton hwButton; - private final MultiStateHardwareLight light; - private final int padId; - private final PadBank bankId; - private final SysExHandler sysExHandler; - private final int noteValue; - - public enum Type { - NOTE, - CC - } - - public RgbButton(final int padId, final PadBank bankId, final Type type, final int value, final int channel, - final boolean shiftButtonType, final MiniLab3Extension driver) { - this.padId = padId; - this.bankId = bankId; - noteValue = value; - final MidiIn midiIn = driver.getMidiIn(); - sysExHandler = driver.getSysExHandler(); - rgbCommand[7] = (byte) 0x2; // DAW Preset - rgbCommand[9] = (byte) padId; - hwButton = driver.getSurface().createHardwareButton("RGB_PAD_" + value); - if (type == Type.NOTE) { - hwButton.pressedAction().setActionMatcher(midiIn.createNoteOnActionMatcher(channel, value)); - hwButton.releasedAction().setActionMatcher(midiIn.createNoteOffActionMatcher(channel, value)); - } else { - hwButton.pressedAction().setActionMatcher(midiIn.createCCActionMatcher(channel, value, 127)); - hwButton.releasedAction().setActionMatcher(midiIn.createCCActionMatcher(channel, value, 0)); - } - hwButton.isPressed().markInterested(); - light = driver.getSurface().createMultiStateHardwareLight("RGB_PAD_LIGHT_" + value); - hwButton.setBackgroundLight(light); - if (bankId.getIndex() == -1) { // Individual updates handled - light.state().onUpdateHardware(this::updateState); - } - } - - public Integer getNoteValue() { - return noteValue; - } - - - public MultiStateHardwareLight getLight() { - return light; - } - - private void updateState(final InternalHardwareLightState state) { - if (state instanceof RgbLightState) { - ((RgbLightState) state).apply(rgbCommand); - sysExHandler.sendColor((byte) padId, bankId, rgbCommand[10], rgbCommand[11], rgbCommand[12]); - } else { - setRgbOff(); - } - } - - private void setRgbOff() { - rgbCommand[10] = 0; - rgbCommand[11] = 0; - rgbCommand[12] = 0; - } - - public void bindPressed(final Layer layer, final Consumer target, - final Supplier lightSource) { - layer.bind(hwButton, hwButton.pressedAction(), () -> target.accept(true)); - layer.bind(hwButton, hwButton.releasedAction(), () -> target.accept(false)); - layer.bindLightState(lightSource::get, light); - } - - public void bind(final Layer layer, final Runnable action, final Supplier lightSource) { - layer.bind(hwButton, hwButton.pressedAction(), action); - layer.bind(hwButton, hwButton.releasedAction(), () -> { - }); - layer.bindLightState(lightSource::get, light); - } - - public void bind(final Layer layer, final Runnable action, final RgbLightState pressOn, - final RgbLightState pressOff) { - layer.bind(hwButton, hwButton.pressedAction(), action); - layer.bind(hwButton, hwButton.releasedAction(), () -> { - }); - layer.bindLightState(() -> hwButton.isPressed().get() ? pressOn : pressOff, light); - } - - public void bindToggle(final Layer layer, final SettableBooleanValue value, final RgbLightState onColor, - final RgbLightState offColor) { - value.markInterested(); - layer.bind(hwButton, hwButton.pressedAction(), value::toggle); - layer.bindLightState(() -> value.get() ? onColor : offColor, light); - } - -} diff --git a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbLightState.java b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbLightState.java index a51b0e1b..ada53b09 100644 --- a/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbLightState.java +++ b/src/main/java/com/bitwig/extensions/controllers/arturia/minilab3/RgbLightState.java @@ -9,67 +9,68 @@ public class RgbLightState extends InternalHardwareLightState { private static final Map indexLookup = new HashMap<>(); - - public static final RgbLightState RED = new RgbLightState(127, 0, 0); + + public static final RgbLightState RED = new RgbLightState(100, 0, 0, true); public static final RgbLightState BLUE = new RgbLightState(0, 0, 127); - public static final RgbLightState WHITE = new RgbLightState(127, 127, 127); + public static final RgbLightState WHITE = new RgbLightState(100, 127, 127, true); public static final RgbLightState OFF = new RgbLightState(0, 0, 0); public static final RgbLightState YELLOW = new RgbLightState(127, 127, 0); public static final RgbLightState ORANGE = new RgbLightState(127, 0x32, 0); public static final RgbLightState ORANGE_DIMMED = new RgbLightState(0x14, 0x05, 0); public static final RgbLightState GREEN = new RgbLightState(0, 127, 0); - public static final RgbLightState GREEN_DIMMED = new RgbLightState(0, 0x14, 0); - public static final RgbLightState RED_DIMMED = new RgbLightState(0x14, 0, 0); + public static final RgbLightState GREEN_DIMMED = new RgbLightState(0, 0x8, 0); + public static final RgbLightState RED_DIMMED = new RgbLightState(0x8, 0, 0); public static final RgbLightState WHITE_DIMMED = new RgbLightState(0x14, 0x14, 0x14); - + private final byte red; private final byte green; private final byte blue; private final HardwareLightVisualState visualState; private final RgbLightState brighter; private final RgbLightState darker; - + public static RgbLightState getColor(final double red, final double green, final double blue) { final int rv = (int) Math.floor(red * 255); final int gv = (int) Math.floor(green * 255); final int bv = (int) Math.floor(blue * 255); - + final int colorLookup = rv << 16 | gv << 8 | bv; - return indexLookup.computeIfAbsent(colorLookup, index -> { - final double[] sat = saturate(red, green, blue, 0.2); - return new RgbLightState(sat[0], sat[1], sat[2], true); - }); + return indexLookup.computeIfAbsent( + colorLookup, index -> { + final double[] sat = saturate(red, green, blue, 0.2); + return new RgbLightState(sat[0], sat[1], sat[2], true); + }); } - + public RgbLightState(final double red, final double green, final double blue, final boolean variants) { this.red = convert(red, 1); this.green = convert(green, 1); this.blue = convert(blue, 1); brighter = new RgbLightState(red * 1.50, green * 1.50, blue * 1.50); - darker = new RgbLightState(red * 0.35, green * 0.3, blue * 0.3); - visualState = HardwareLightVisualState.createForColor( - Color.fromRGB(this.red << 1, this.green << 1, this.blue << 1)); + darker = new RgbLightState(red * 0.2, green * 0.2, blue * 0.2); + visualState = + HardwareLightVisualState.createForColor(Color.fromRGB(this.red << 1, this.green << 1, this.blue << 1)); } - + public RgbLightState(final double red, final double green, final double blue) { this.red = convert(red, 1.0); this.green = convert(green, 1.0); this.blue = convert(blue, 1.0); brighter = this; darker = this; - visualState = HardwareLightVisualState.createForColor( - Color.fromRGB(this.red << 1, this.green << 1, this.blue << 1)); + visualState = + HardwareLightVisualState.createForColor(Color.fromRGB(this.red << 1, this.green << 1, this.blue << 1)); } - + public RgbLightState(final int red, final int green, final int blue, final boolean variants) { this.red = convert(red); this.green = convert(green); this.blue = convert(blue); brighter = new RgbLightState(Math.round(red * 1.10), green + 10, blue + 10); - darker = new RgbLightState(red * 0.3, green * 0.3, blue * 0.3); + darker = new RgbLightState(red * 0.05, green * 0.05, blue * 0.05); visualState = HardwareLightVisualState.createForColor(Color.fromRGB(red << 1, green << 1, blue << 1)); } - + public RgbLightState(final int red, final int green, final int blue) { this.red = convert(red); this.green = convert(green); @@ -78,7 +79,7 @@ public RgbLightState(final int red, final int green, final int blue) { darker = this; visualState = HardwareLightVisualState.createForColor(Color.fromRGB(red << 1, green << 1, blue << 1)); } - + public byte convert(final double colorValue, final double factor) { final int value = (int) Math.floor(colorValue * 100 * factor); if (value >= 0 && value <= 127) { @@ -89,7 +90,7 @@ public byte convert(final double colorValue, final double factor) { } return 127; } - + public byte convert(final int value) { if (value >= 0 && value <= 127) { return (byte) value; @@ -99,32 +100,32 @@ public byte convert(final int value) { } return 127; } - + @Override public HardwareLightVisualState getVisualState() { return visualState; } - + public void apply(final byte[] rgbCommand) { rgbCommand[10] = red; rgbCommand[11] = green; rgbCommand[12] = blue; } - + public void apply(final int bankOffset, final byte[] rgbCommand) { rgbCommand[bankOffset * 3] = red; rgbCommand[bankOffset * 3 + 1] = green; rgbCommand[bankOffset * 3 + 2] = blue; } - + public RgbLightState getBrighter() { return brighter; } - + public RgbLightState getDarker() { return darker; } - + @Override public boolean equals(final Object obj) { if (this == obj) { @@ -139,12 +140,12 @@ public boolean equals(final Object obj) { final RgbLightState other = (RgbLightState) obj; return blue == other.blue && green == other.green && red == other.red; } - + @Override public String toString() { return "RgbLightState{" + "red=" + red + ", green=" + green + ", blue=" + blue + '}'; } - + private static double[] saturate(final double red, final double green, final double blue, final double amount) { final double max = Math.max(Math.max(red, green), blue); final double min = Math.min(Math.min(red, green), blue); @@ -166,19 +167,19 @@ private static double[] saturate(final double red, final double green, final dou } else if (blue == min) { bv = Math.max(0.0, blue - amount); } - - return new double[]{rv, gv, bv}; + + return new double[] {rv, gv, bv}; } - - + + public byte getRed() { return red; } - + public byte getBlue() { return blue; } - + public byte getGreen() { return green; } diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/LaunchControlMidiProcessor.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/LaunchControlMidiProcessor.java index d70e51f0..6ba435ae 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/LaunchControlMidiProcessor.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/LaunchControlMidiProcessor.java @@ -127,6 +127,7 @@ private void handleMidiIn(final int status, final int data1, final int data2) { this.modeListeners.forEach(listener -> listener.accept(mode)); } } + LaunchControlMk3Extension.println(" MIDI %02X %02X %02X", status, data1, data2); } public void sendRgb(final int index, final RgbColor color) { diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/BooleanLightValueBinding.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/BooleanLightValueBinding.java index 9842fa00..fa2dbe55 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/BooleanLightValueBinding.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/BooleanLightValueBinding.java @@ -13,7 +13,7 @@ public class BooleanLightValueBinding extends Binding public BooleanLightValueBinding(final LaunchLight light, final BooleanValue value, final RgbColor onColor, final RgbColor offColor) { - super(light, value, light); + super(light.getLightId(), value, light); this.onColor = onColor; this.offColor = offColor; value.addValueObserver(this::handleValue); diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/ParameterDisplayBinding.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/ParameterDisplayBinding.java index 05688303..8d26185c 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/ParameterDisplayBinding.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/ParameterDisplayBinding.java @@ -10,7 +10,6 @@ public class ParameterDisplayBinding extends Binding imple private final DisplayControl display; private final int targetId; - private long incTime = 0; private String titleName; private String parameterName; private String displayValue; @@ -28,10 +27,6 @@ public ParameterDisplayBinding(final DisplayId displayId, final StringValue titl this.parameterName = parameter.name().get(); } - public void notifyInc() { - incTime = System.currentTimeMillis(); - } - private void handleTrackName(final String trackName) { this.titleName = trackName; if (isActive() && !disabled) { @@ -40,15 +35,10 @@ private void handleTrackName(final String trackName) { } } - private void handleParamNameChanged(final String value) { this.parameterName = value; if (isActive() && !disabled) { display.setText(targetId, 1, parameterName); - final long diff = System.currentTimeMillis() - incTime; - if (diff < 200) { - display.showDisplay(targetId); - } } } @@ -56,10 +46,6 @@ private void handleDisplayValue(final String value) { this.displayValue = value; if (isActive() && !disabled) { display.setText(targetId, 2, displayValue); - final long diff = System.currentTimeMillis() - incTime; - if (diff < 200) { - display.showDisplay(targetId); - } } } @@ -87,6 +73,7 @@ protected void activate() { if (disabled) { return; } + display.configureDisplay(targetId, 0x62); display.setText(targetId, 0, titleName); display.setText(targetId, 1, parameterName); display.setText(targetId, 2, displayValue); diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/RelativeDisplayControl.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/RelativeDisplayControl.java index e11674c6..6596c6ca 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/RelativeDisplayControl.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/RelativeDisplayControl.java @@ -58,9 +58,6 @@ private void handleParamNameChanged(final String value) { if (isActive()) { display.setText(targetId, 1, paramName); final long diff = System.currentTimeMillis() - incTime; - if (diff < 200) { - display.showDisplay(targetId); - } } } @@ -68,10 +65,6 @@ private void handleDisplayValue(final String value) { this.displayValue = value; if (isActive()) { display.setText(targetId, 2, displayValue); - final long diff = System.currentTimeMillis() - incTime; - if (diff < 200) { - display.showDisplay(targetId); - } } } @@ -89,6 +82,7 @@ protected void deactivate() { @Override protected void activate() { + display.configureDisplay(targetId, 0x62); display.setText(targetId, 0, title); display.setText(targetId, 1, paramName); display.setText(targetId, 2, displayValue); diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/SegmentDisplayBinding.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/SegmentDisplayBinding.java index f74181cf..c67af37e 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/SegmentDisplayBinding.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/bindings/SegmentDisplayBinding.java @@ -1,6 +1,7 @@ package com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.bindings; import com.bitwig.extension.controller.api.StringValue; +import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.LaunchControlMk3Extension; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.display.DisplaySegment; import com.bitwig.extensions.framework.Binding; import com.bitwig.extensions.framework.values.BasicStringValue; @@ -34,6 +35,7 @@ private boolean isBlocked() { private void handleNameUpdate(final String name) { this.name = name; if (isActive() && !isBlocked()) { + LaunchControlMk3Extension.println(" NAME UPDATE " + name); getTarget().show2LinesBuffered(title, name); } } @@ -51,6 +53,5 @@ protected void deactivate() { @Override protected void activate() { - } } diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/control/LaunchLight.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/control/LaunchLight.java index e0d43d28..03a24afa 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/control/LaunchLight.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/control/LaunchLight.java @@ -41,7 +41,7 @@ private void updateStateCC(final InternalHardwareLightState state) { // break; // } } else { - midiProcessor.sendMidi(0xB0, midiId, 0); + //midiProcessor.sendMidi(0xB0, midiId, 0); } } diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/definition/LaunchControlExtensionDefinition.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/definition/LaunchControlExtensionDefinition.java index ca2bd981..3e56e293 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/definition/LaunchControlExtensionDefinition.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/definition/LaunchControlExtensionDefinition.java @@ -20,7 +20,7 @@ public boolean isXlVersion() { @Override public String getName() { - return "Launch Control Mk3"; + return "Launch Control 3"; } @Override @@ -30,12 +30,12 @@ public UUID getId() { @Override public String getHardwareModel() { - return "Launch Control Mk3"; + return "Launch Control 3"; } @Override public String getHelpFilePath() { - return "Controllers/Novation/Launch Control XL Mk3.pdf"; + return "Controllers/Novation/Launch Control 3.pdf"; } @Override diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/definition/LaunchControlXlExtensionDefinition.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/definition/LaunchControlXlExtensionDefinition.java index 5cfda5fa..7e3c09fe 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/definition/LaunchControlXlExtensionDefinition.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/definition/LaunchControlXlExtensionDefinition.java @@ -20,7 +20,7 @@ public boolean isXlVersion() { @Override public String getName() { - return "Launch Control XL Mk3"; + return "Launch Control XL 3"; } @Override diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/LcMixerLayer.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/LcMixerLayer.java index c6e39dac..aae972f2 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/LcMixerLayer.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/LcMixerLayer.java @@ -10,6 +10,7 @@ import com.bitwig.extensions.controllers.novation.commonsmk3.RgbState; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.CcConstValues; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.LaunchControlMidiProcessor; +import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.LaunchControlMk3Extension; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.LaunchControlXlHwElements; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.LaunchViewControl; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.bindings.AbsoluteEncoderBinding; @@ -57,6 +58,7 @@ public LcMixerLayer(final Layers layers, final LaunchControlMidiProcessor midiPr final LaunchViewControl viewControl, final LaunchControlXlHwElements hwElements, final DisplayControl displayControl, final TransportHandler transportHandler, final ButtonLayers buttonLayers) { super(layers, midiProcessor, host, viewControl, hwElements, displayControl, transportHandler, buttonLayers); + LaunchControlMk3Extension.println(" LC MIXER LAYER"); panLayer = new Layer(layers, "PAN"); sendLayer = new Layer(layers, "SEND"); @@ -184,9 +186,8 @@ private void bindTrack(final LaunchControlXlHwElements hwElements, final TrackBa new ParameterDisplayBinding( new DisplayId(relativeRow2Encoder.getTargetId(), displayControl), track.name(), track.pan()); panLayer.addBinding(panDisplayBinding); - panLayer.addBinding( - new LightValueBindings(track.pan(), hwElements.getRelativeEncoder(0, index).getLight(), GradientColor.PAN)); - panLayer.addBinding(new RelativeEncoderBinding(track.pan(), hwElements.getRelativeEncoder(0, index))); + panLayer.addBinding(new LightValueBindings(track.pan(), relativeRow2Encoder.getLight(), GradientColor.PAN)); + panLayer.addBinding(new RelativeEncoderBinding(track.pan(), relativeRow2Encoder)); final LaunchButton button = hwElements.getRowButtons(0, index); button.bindLight(this.buttonLayers.getSelectLayer(), () -> selectColor(track, index)); @@ -206,17 +207,17 @@ private void buttonModePressed(final boolean pressed) { } switch (this.buttonMode) { case SELECT -> { - buttonMode = ButtonMode.SOLO; - displayControl.show2LineTemporary("Button Function", "Solo"); - } - case SOLO -> { buttonMode = ButtonMode.MUTE; displayControl.show2LineTemporary("Button Function", "Mute"); } - case MUTE -> { + case SOLO -> { buttonMode = ButtonMode.ARM; displayControl.show2LineTemporary("Button Function", "Arm"); } + case MUTE -> { + buttonMode = ButtonMode.SOLO; + displayControl.show2LineTemporary("Button Function", "Solo"); + } case ARM -> { buttonMode = ButtonMode.SELECT; displayControl.show2LineTemporary("Button Function", "Select"); diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/SpecControl.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/SpecControl.java index db623d34..702d4742 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/SpecControl.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/SpecControl.java @@ -37,7 +37,7 @@ protected void handlePanelLayoutUpdate(final LayoutType newValue) { public void setActive(final boolean active) { specLayer.setIsActive(active); if (active) { - displayControl.show2LineTemporary("Button Row 2", "Transport Control"); + displayControl.show2LineTemporary("Mode", "Transport"); specLauncherLayer.setIsActive(transportHandler.getPanelLayout().get() == LayoutType.LAUNCHER); } else { specLauncherLayer.setIsActive(false); diff --git a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/XlMixerLayer.java b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/XlMixerLayer.java index ce9bf014..84340a95 100644 --- a/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/XlMixerLayer.java +++ b/src/main/java/com/bitwig/extensions/controllers/novation/launchcontrolxlmk3/layer/XlMixerLayer.java @@ -2,6 +2,7 @@ import com.bitwig.extension.controller.api.ControllerHost; import com.bitwig.extension.controller.api.CursorTrack; +import com.bitwig.extension.controller.api.HardwareSlider; import com.bitwig.extension.controller.api.Send; import com.bitwig.extension.controller.api.SendBank; import com.bitwig.extension.controller.api.Track; @@ -10,9 +11,11 @@ import com.bitwig.extensions.controllers.novation.commonsmk3.RgbState; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.CcConstValues; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.LaunchControlMidiProcessor; +import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.LaunchControlMk3Extension; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.LaunchControlXlHwElements; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.LaunchViewControl; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.bindings.AbsoluteEncoderBinding; +import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.bindings.ControlTargetId; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.bindings.DisplayId; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.bindings.LightSendValueBindings; import com.bitwig.extensions.controllers.novation.launchcontrolxlmk3.bindings.LightValueBindings; @@ -55,6 +58,7 @@ public XlMixerLayer(final Layers layers, final LaunchControlXlHwElements hwEleme final LaunchControlMidiProcessor midiProcessor, final ControllerHost host, final TransportHandler transportHandler, final ButtonLayers buttonLayers) { super(layers, midiProcessor, host, viewControl, hwElements, displayControl, transportHandler, buttonLayers); + LaunchControlMk3Extension.println(" XL MIXER LAYER"); final TrackBank trackBank = viewControl.getTrackBank(); for (int i = 0; i < 8; i++) { @@ -149,11 +153,12 @@ private void bindTrack(final LaunchControlXlHwElements hwElements, final TrackBa mixerLayer.addBinding(new RelativeEncoderBinding(track.pan(), row3Encoder)); // fixedVolumeLabel + final ControlTargetId sliderId = new ControlTargetId(index); final ParameterDisplayBinding volumeDisplayBinding = - new ParameterDisplayBinding( - new DisplayId(row2Encoder.getTargetId(), displayControl), track.name(), track.volume()); + new ParameterDisplayBinding(new DisplayId(index + 5, displayControl), track.name(), track.volume()); mixerLayer.addBinding(volumeDisplayBinding); - this.addBinding(new SliderBinding(row2Encoder.getId(), track.volume(), hwElements.getSlider(index))); + final HardwareSlider slider = hwElements.getSlider(index); + this.addBinding(new SliderBinding(sliderId, track.volume(), slider)); final LaunchButton row2Button = hwElements.getRowButtons(1, index); final LaunchButton row1Button = hwElements.getRowButtons(0, index); diff --git a/src/main/resources/Documentation/Controllers/Novation/Launch Control 3.pdf b/src/main/resources/Documentation/Controllers/Novation/Launch Control 3.pdf new file mode 100644 index 00000000..b2a80047 Binary files /dev/null and b/src/main/resources/Documentation/Controllers/Novation/Launch Control 3.pdf differ diff --git a/src/main/resources/Documentation/Controllers/Novation/Launch Control XL 3.pdf b/src/main/resources/Documentation/Controllers/Novation/Launch Control XL 3.pdf new file mode 100644 index 00000000..a7f5f5ea Binary files /dev/null and b/src/main/resources/Documentation/Controllers/Novation/Launch Control XL 3.pdf differ diff --git a/src/main/resources/Documentation/Controllers/Novation/Launch Control XL Mk3.pdf b/src/main/resources/Documentation/Controllers/Novation/Launch Control XL Mk3.pdf deleted file mode 100644 index 67ad9aa1..00000000 Binary files a/src/main/resources/Documentation/Controllers/Novation/Launch Control XL Mk3.pdf and /dev/null differ diff --git a/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition b/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition index 24de40af..01e4e10c 100644 --- a/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition +++ b/src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition @@ -23,6 +23,7 @@ com.bitwig.extensions.controllers.arturia.keylab.essential.ArturiaKeylabEssentia com.bitwig.extensions.controllers.arturia.keylab.essentialMk3.KeyLabEssential3ExtensionDefinition com.bitwig.extensions.controllers.arturia.microlab.ArturiaMicroLabControllerExtensionDefinition com.bitwig.extensions.controllers.arturia.minilab3.MiniLab3ExtensionDefinition +#com.bitwig.extensions.controllers.arturia.minilab3.MiniLab37ExtensionDefinition com.bitwig.extensions.controllers.arturia.beatsteppro.BeatStepProExtensionDefinition com.bitwig.extensions.controllers.arturia.keystep.KeyStepProExtensionDefinition com.bitwig.extensions.controllers.arturia.keystep.KeyStepExtensionDefinition