From c834019f3212183a613a47c09535cc84a9fcfc5a Mon Sep 17 00:00:00 2001 From: ericahrens Date: Tue, 27 Feb 2024 20:21:48 +0100 Subject: [PATCH] MCU fixed issue with non-ascii characters causin extension to crash + EQ+ navigation improvements --- .../controllers/mcu/StringUtil.java | 62 ++++--- .../display/ParameterDisplayBinding.java | 32 ---- .../display/ParameterValueDisplayBinding.java | 5 - .../display/StringDisplayBinding.java | 5 +- .../display/StringRowDisplayBinding.java | 5 +- .../mcu/devices/DeviceManager.java | 28 +-- .../mcu/devices/DeviceTypeFollower.java | 40 ++-- .../mcu/devices/SpecificDevice.java | 2 +- .../controllers/mcu/display/LcdDisplay.java | 171 +++++++++--------- .../mcu/layer/SpecialDeviceModeLayer.java | 14 ++ .../controllers/mcu/value/BasicIntValue.java | 30 +++ .../Controllers/SSL/SSL UF8-UF1.pdf | Bin 1793888 -> 1794268 bytes 12 files changed, 218 insertions(+), 176 deletions(-) delete mode 100644 src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/ParameterDisplayBinding.java create mode 100644 src/main/java/com/bitwig/extensions/controllers/mcu/value/BasicIntValue.java diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/StringUtil.java b/src/main/java/com/bitwig/extensions/controllers/mcu/StringUtil.java index bd0508d0..69421387 100644 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/StringUtil.java +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/StringUtil.java @@ -1,22 +1,26 @@ package com.bitwig.extensions.controllers.mcu; public class StringUtil { - + private static final int PAN_RANGE = 50; - private static final char[] SPECIALS = {'ä', 'ü', 'ö', 'Ä', 'Ü', 'Ö', 'ß', 'é', 'è', 'ê', 'â', 'á', 'à', // - 'û', 'ú', 'ù', 'ô', 'ó', 'ò'}; - private static final String[] REPLACE = {"a", "u", "o", "A", "U", "O", "ss", "e", "e", "e", "a", "a", "a", // - "u", "u", "u", "o", "o", "o"}; - + private static final char[] SPECIALS = { + 'ä', 'ü', 'ö', 'Ä', 'Ü', 'Ö', 'ß', 'é', 'è', 'ê', 'â', 'á', 'à', // + 'û', 'ú', 'ù', 'ô', 'ó', 'ò' + }; + private static final String[] REPLACE = { + "a", "u", "o", "A", "U", "O", "ss", "e", "e", "e", "a", "a", "a", // + "u", "u", "u", "o", "o", "o" + }; + private StringUtil() { } - + public static String toBarBeats(final double value) { final int bars = (int) Math.floor(value); final int beats = (int) Math.floor((value - bars) * 4); return String.format("%02d:%02d", bars, beats); } - + public static String panToString(final double v) { final int intv = (int) (v * PAN_RANGE * 2); if (intv == PAN_RANGE) { @@ -26,7 +30,7 @@ public static String panToString(final double v) { } return " " + (intv - PAN_RANGE) + "R"; } - + /** * Tailored to condense Volume value strings. Removes leading + and spaces. * @@ -45,27 +49,27 @@ public static String condenseVolumeValue(final String valueText, final int maxLe } return sb.toString(); } - + public static String toTwoCharVal(final int value) { if (value < 10) { return " " + value; } return Integer.toString(value); } - + public static String toDisplayName(final String text) { if (text.length() < 2) { return text; } return text.charAt(0) + text.substring(1, Math.min(6, text.length())).toLowerCase(); } - - + + public static String padString(final String text, final int pad) { return " ".repeat(Math.max(0, pad)) + text; } - - public static String padEnd(final String text, int paddingLength) { + + public static String padEnd(final String text, final int paddingLength) { if (text.length() == paddingLength) { return text; } @@ -74,19 +78,35 @@ public static String padEnd(final String text, int paddingLength) { } return text + " ".repeat(paddingLength - text.length()); } - + public static String limit(final String value, final int max) { return value.substring(0, Math.min(max, value.length())); } - + public static String reduceAscii(final String name, final int maxLen) { - String result = toAsciiDisplay(name, maxLen + 10); + final String result = toAsciiDisplay(name, maxLen + 10); if (result.length() <= maxLen) { return result; } return result.replace(" ", ""); } - + + public static String toAscii(final String value) { + final StringBuilder b = new StringBuilder(); + for (int i = 0; i < value.length(); i++) { + final char c = value.charAt(i); + if (c < 128) { + b.append(c); + } else { + final int replacement = getReplace(c); + if (replacement >= 0) { + b.append(REPLACE[replacement]); + } + } + } + return b.toString(); + } + public static String toAsciiDisplay(final String name, final int maxLen) { final StringBuilder b = new StringBuilder(); for (int i = 0; i < name.length() && b.length() < maxLen; i++) { @@ -105,7 +125,7 @@ public static String toAsciiDisplay(final String name, final int maxLen) { } return b.toString(); } - + private static int getReplace(final char c) { for (int i = 0; i < SPECIALS.length; i++) { if (c == SPECIALS[i]) { @@ -114,5 +134,5 @@ private static int getReplace(final char c) { } return -1; } - + } diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/ParameterDisplayBinding.java b/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/ParameterDisplayBinding.java deleted file mode 100644 index 9cbd04f9..00000000 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/ParameterDisplayBinding.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.bitwig.extensions.controllers.mcu.bindings.display; - -import com.bitwig.extension.controller.api.Parameter; -import com.bitwig.extensions.controllers.mcu.display.DisplayManager; -import com.bitwig.extensions.controllers.mcu.layer.ControlMode; -import com.bitwig.extensions.controllers.mcu.value.StringValueConverter; - -public class ParameterDisplayBinding extends AbstractDisplayBinding { - private final StringValueConverter converter; - - public ParameterDisplayBinding(final DisplayManager target, final ControlMode mode, - final DisplayTarget displayTargetIndex, final Parameter parameter) { - this(target, mode, displayTargetIndex, parameter, s -> s); - } - - public ParameterDisplayBinding(final DisplayManager target, final ControlMode mode, - final DisplayTarget displayTargetIndex, final Parameter parameter, final StringValueConverter converter) { - super(target, mode, displayTargetIndex, parameter); - parameter.exists().addValueObserver(this::handleExists); - parameter.displayedValue().addValueObserver(this::handleValueChange); - this.converter = converter; - this.lastValue = converter.convert(parameter.displayedValue().get()); - } - - private void handleValueChange(final String newValue) { - this.lastValue = newValue; - if (isActive()) { - updateDisplay(); - } - } - -} diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/ParameterValueDisplayBinding.java b/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/ParameterValueDisplayBinding.java index ede6e417..394c4970 100644 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/ParameterValueDisplayBinding.java +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/ParameterValueDisplayBinding.java @@ -8,11 +8,6 @@ public class ParameterValueDisplayBinding extends AbstractDisplayBinding { private final DoubleValueConverter converter; - public ParameterValueDisplayBinding(final DisplayManager target, final ControlMode mode, - final DisplayTarget displayTargetIndex, final Parameter parameter) { - this(target, mode, displayTargetIndex, parameter, value -> Double.toString(value)); - } - public ParameterValueDisplayBinding(final DisplayManager target, final ControlMode mode, final DisplayTarget displayTargetIndex, final Parameter parameter, final DoubleValueConverter converter) { super(target, mode, displayTargetIndex, parameter); diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/StringDisplayBinding.java b/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/StringDisplayBinding.java index f2f81485..994d3799 100644 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/StringDisplayBinding.java +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/StringDisplayBinding.java @@ -2,6 +2,7 @@ import com.bitwig.extension.controller.api.BooleanValue; import com.bitwig.extension.controller.api.StringValue; +import com.bitwig.extensions.controllers.mcu.StringUtil; import com.bitwig.extensions.controllers.mcu.bindings.ResetableBinding; import com.bitwig.extensions.controllers.mcu.display.DisplayManager; import com.bitwig.extensions.controllers.mcu.layer.ControlMode; @@ -24,7 +25,7 @@ public StringDisplayBinding(final DisplayManager target, final ControlMode mode, public StringDisplayBinding(final DisplayManager target, final ControlMode mode, final DisplayTarget displayTargetIndex, final StringValue stringValue, final BooleanValue existsValue) { - this(target, mode, displayTargetIndex, stringValue, existsValue, s -> s); + this(target, mode, displayTargetIndex, stringValue, existsValue, s -> StringUtil.toAsciiDisplay(s, 8)); } private void handleValueChange(final String newValue) { @@ -36,7 +37,7 @@ private void handleValueChange(final String newValue) { @Override public void reset() { - this.lastValue = getSource().get(); + this.lastValue = stringConversion.convert(getSource().get()); if (isActive()) { updateDisplay(); } diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/StringRowDisplayBinding.java b/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/StringRowDisplayBinding.java index bf6e003c..c00b2e48 100644 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/StringRowDisplayBinding.java +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/bindings/display/StringRowDisplayBinding.java @@ -1,6 +1,7 @@ package com.bitwig.extensions.controllers.mcu.bindings.display; import com.bitwig.extension.controller.api.StringValue; +import com.bitwig.extensions.controllers.mcu.StringUtil; import com.bitwig.extensions.controllers.mcu.display.DisplayManager; import com.bitwig.extensions.controllers.mcu.display.DisplayRow; import com.bitwig.extensions.controllers.mcu.layer.ControlMode; @@ -12,7 +13,7 @@ public StringRowDisplayBinding(final DisplayManager target, final ControlMode mo super(target, mode, DisplayTarget.of(row.getRowIndex(), -1, sectionIndex), stringValue); exists = true; stringValue.addValueObserver(this::handleValueChange); - this.lastValue = stringValue.get(); + this.lastValue = StringUtil.toAscii(stringValue.get()); } protected void updateDisplay() { @@ -20,7 +21,7 @@ protected void updateDisplay() { } private void handleValueChange(final String newValue) { - this.lastValue = newValue; + this.lastValue = StringUtil.toAscii(newValue); if (isActive()) { updateDisplay(); } diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/devices/DeviceManager.java b/src/main/java/com/bitwig/extensions/controllers/mcu/devices/DeviceManager.java index 235132f9..55584c31 100644 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/devices/DeviceManager.java +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/devices/DeviceManager.java @@ -3,31 +3,31 @@ import com.bitwig.extension.controller.api.Parameter; public interface DeviceManager { - + //void initiateBrowsing(final BrowserConfiguration browser, Type type); - + //void addBrowsing(final BrowserConfiguration browser, boolean after); - + //void setInfoLayer(DisplayLayer infoLayer); - + //void enableInfo(InfoSource type); - + void disableInfo(); - + //InfoSource getInfoSource(); - + Parameter getParameter(int index); - + //ParameterPage getParameterPage(int index); - + void navigateDeviceParameters(final int direction); - + //void handleResetInvoked(final int index, final ModifierValueObject modifier); - DeviceTypeFollower getCurrentFollower(); - + DeviceTypeFollower getDeviceFollower(); + boolean isSpecificDevicePresent(); - + int getPageCount(); - + } diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/devices/DeviceTypeFollower.java b/src/main/java/com/bitwig/extensions/controllers/mcu/devices/DeviceTypeFollower.java index 503b6c34..6fc66512 100644 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/devices/DeviceTypeFollower.java +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/devices/DeviceTypeFollower.java @@ -1,9 +1,15 @@ package com.bitwig.extensions.controllers.mcu.devices; import com.bitwig.extension.callback.BooleanValueChangedCallback; -import com.bitwig.extension.controller.api.*; +import com.bitwig.extension.controller.api.BooleanValue; +import com.bitwig.extension.controller.api.CursorTrack; +import com.bitwig.extension.controller.api.Device; +import com.bitwig.extension.controller.api.DeviceBank; +import com.bitwig.extension.controller.api.DeviceMatcher; +import com.bitwig.extension.controller.api.PinnableCursorDevice; import com.bitwig.extensions.controllers.mcu.CursorDeviceControl; import com.bitwig.extensions.controllers.mcu.VPotMode; +import com.bitwig.extensions.controllers.mcu.value.BasicIntValue; import com.bitwig.extensions.framework.values.BooleanValueObject; public class DeviceTypeFollower { @@ -12,20 +18,24 @@ public class DeviceTypeFollower { private final Device focusDevice; private final CursorDeviceControl cursorDeviceControl; private final BooleanValue cursorOnDevice; + private final BasicIntValue trackIndex = new BasicIntValue(); + private int chainIndex = -1; - + public DeviceTypeFollower(final CursorDeviceControl cursorDeviceControl, final DeviceMatcher matcher, - final VPotMode potMode) { + final VPotMode potMode) { this.cursorDeviceControl = cursorDeviceControl; final CursorTrack cursorTrack = cursorDeviceControl.getCursorTrack(); deviceBank = cursorTrack.createDeviceBank(1); this.potMode = potMode; deviceBank.setDeviceMatcher(matcher); - + focusDevice = deviceBank.getItemAt(0); focusDevice.exists().markInterested(); focusDevice.name().markInterested(); - + + cursorTrack.position().addValueObserver(pos -> trackIndex.set(pos)); + final PinnableCursorDevice cursorDevice = cursorDeviceControl.getCursorDevice(); if (potMode.getAssign() == VPotMode.BitwigType.DEVICE) { final BooleanValueObject matchesTyp = new BooleanValueObject(); @@ -50,19 +60,19 @@ public DeviceTypeFollower(final CursorDeviceControl cursorDeviceControl, final D } }); } - + public void addOnCursorListener(final BooleanValueChangedCallback booleanValueChangedCallback) { cursorOnDevice.addValueObserver(booleanValueChangedCallback); } - + public Device getFocusDevice() { return focusDevice; } - + public Device getCurrentDevice() { return cursorDeviceControl.getCursorDevice(); } - + public void addNewDeviceAfter() { if (focusDevice.exists().get()) { focusDevice.afterDeviceInsertionPoint().browse(); @@ -70,7 +80,7 @@ public void addNewDeviceAfter() { deviceBank.browseToInsertDevice(0); } } - + public void addNewDeviceBefore() { if (focusDevice.exists().get()) { focusDevice.beforeDeviceInsertionPoint().browse(); @@ -78,7 +88,7 @@ public void addNewDeviceBefore() { deviceBank.browseToInsertDevice(0); } } - + public void initiateBrowsing() { if (focusDevice.exists().get()) { focusDevice.replaceDeviceInsertionPoint().browse(); @@ -86,12 +96,14 @@ public void initiateBrowsing() { deviceBank.browseToInsertDevice(0); } } - + public void ensurePosition() { - final int currentDiveChainLocation = cursorDeviceControl.getCursorDevice().position().get(); if (!cursorOnDevice.get()) { cursorDeviceControl.getCursorDevice().selectDevice(focusDevice); } } - + + public BasicIntValue getTrackIndex() { + return trackIndex; + } } diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/devices/SpecificDevice.java b/src/main/java/com/bitwig/extensions/controllers/mcu/devices/SpecificDevice.java index 6d68b579..8a120bd3 100644 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/devices/SpecificDevice.java +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/devices/SpecificDevice.java @@ -81,7 +81,7 @@ public BooleanValue getExists() { } @Override - public DeviceTypeFollower getCurrentFollower() { + public DeviceTypeFollower getDeviceFollower() { return deviceFollower; } diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/display/LcdDisplay.java b/src/main/java/com/bitwig/extensions/controllers/mcu/display/LcdDisplay.java index a922ffda..2157508d 100644 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/display/LcdDisplay.java +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/display/LcdDisplay.java @@ -1,5 +1,8 @@ package com.bitwig.extensions.controllers.mcu.display; +import java.util.Arrays; +import java.util.List; + import com.bitwig.extension.controller.api.HardwareSurface; import com.bitwig.extension.controller.api.HardwareTextDisplay; import com.bitwig.extension.controller.api.MidiOut; @@ -10,87 +13,84 @@ import com.bitwig.extensions.framework.di.Context; import com.bitwig.extensions.framework.values.Midi; -import java.util.Arrays; -import java.util.List; - /** * Represents 2x56 LCD display on the MCU or an extender. */ public class LcdDisplay { private static final int DISPLAY_LEN = 55; private static final int ROW2_START = 56; - + private final byte[] rowDisplayBuffer = { // - (byte) 0XF0, 0, 0, 0X66, 0x14, 0x12, 0, // z: the grid number Zone number 0-3 * 28 - 32, 32, 32, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars - 0, 0, 0, 0, 0, 32, 32, (byte) 247}; - + (byte) 0XF0, 0, 0, 0X66, 0x14, 0x12, 0, // z: the grid number Zone number 0-3 * 28 + 32, 32, 32, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars + 0, 0, 0, 0, 0, 32, 32, (byte) 247 + }; + private final byte[] segBuffer; private final byte[] segBufferExp; private final DisplayPart part; private final HardwareTextDisplay displayRep; private final boolean topRowFlipped; - public String sysHead; - - private final String[][] lastSendGrids = new String[][]{{"", "", "", "", "", "", "", "", ""}, // - {"", "", "", "", "", "", "", "", ""}}; - private final String[] lastSentRows = new String[]{"", ""}; - private final boolean[] fullTextMode = new boolean[]{false, false}; - + private final String[][] lastSendGrids = new String[][] { + {"", "", "", "", "", "", "", "", ""}, // + {"", "", "", "", "", "", "", "", ""} + }; + private final String[] lastSentRows = new String[] {"", ""}; + private final boolean[] fullTextMode = new boolean[] {false, false}; private final MidiOut midiOut; - private VuMode vuMode = VuMode.LED; - private boolean displayBarGraphEnabled = true; private boolean isLowerDisplay; - private final int displayLen; - private final int segmentLength; private final int segmentOffset; private final boolean hasDedicatedVu; - private final char[][] lines = new char[2][60]; - + public String sysHead; + public LcdDisplay(final Context context, final int sectionIndex, final MidiOut midiOut, final SectionType type, - final DisplayPart part, ControllerConfig controllerConfig) { + final DisplayPart part, final ControllerConfig controllerConfig) { this.midiOut = midiOut; this.hasDedicatedVu = controllerConfig.isHasDedicateVu(); this.topRowFlipped = controllerConfig.isTopDisplayRowsFlipped(); this.part = part; - HardwareSurface surface = context.getService(HardwareSurface.class); - GlobalStates states = context.getService(GlobalStates.class); + final HardwareSurface surface = context.getService(HardwareSurface.class); + final GlobalStates states = context.getService(GlobalStates.class); displayRep = surface.createHardwareTextDisplay("DISPLAY_SIMU_" + part + "_" + sectionIndex, 2); - + //initSimulation(driver, sectionIndex, part); - + if (part == DisplayPart.LOWER) { isLowerDisplay = true; rowDisplayBuffer[3] = 0X67; rowDisplayBuffer[4] = 0x15; rowDisplayBuffer[5] = 0x13; sysHead = "f0 00 00 67 15 "; - segBuffer = new byte[]{ // - (byte) 240, 0, 0, 0X67, 0x15, 0x13, 0, // z: the grid number Zone number 0-3 * 28 - 0, 0, 0, 0, 0, 32, // 7: 10 Chars - (byte) 247}; - segBufferExp = new byte[]{ // - (byte) 240, 0, 0, 0X67, 0x15, 0x13, 0, // z: the grid number Zone number 0-3 * 28 - 0, 0, 0, 0, 0, 0, 32, // 7: 10 Chars - (byte) 247}; + segBuffer = new byte[] { // + (byte) 240, 0, 0, 0X67, 0x15, 0x13, 0, // z: the grid number Zone number 0-3 * 28 + 0, 0, 0, 0, 0, 32, // 7: 10 Chars + (byte) 247 + }; + segBufferExp = new byte[] { // + (byte) 240, 0, 0, 0X67, 0x15, 0x13, 0, // z: the grid number Zone number 0-3 * 28 + 0, 0, 0, 0, 0, 0, 32, // 7: 10 Chars + (byte) 247 + }; } else { - segBuffer = new byte[]{ // - (byte) 240, 0, 0, 102, 20, 18, 0, // z: the grid number Zone number 0-3 * 28 - 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars - (byte) 247}; - segBufferExp = new byte[]{ // - (byte) 240, 0, 0, 102, 20, 18, 0, // z: the grid number Zone number 0-3 * 28 - 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars - (byte) 247}; + segBuffer = new byte[] { // + (byte) 240, 0, 0, 102, 20, 18, 0, // z: the grid number Zone number 0-3 * 28 + 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars + (byte) 247 + }; + segBufferExp = new byte[] { // + (byte) 240, 0, 0, 102, 20, 18, 0, // z: the grid number Zone number 0-3 * 28 + 0, 0, 0, 0, 0, 0, 0, // 7: 10 Chars + (byte) 247 + }; if (type == SectionType.XTENDER) { rowDisplayBuffer[4] = 0x15; segBuffer[4] = 0x15; @@ -100,7 +100,7 @@ public LcdDisplay(final Context context, final int sectionIndex, final MidiOut m } } displayLen = LcdDisplay.DISPLAY_LEN + (part == DisplayPart.LOWER && type != SectionType.XTENDER ? 1 : 0); - + if (part == DisplayPart.LOWER) { segmentLength = 6; segmentOffset = 2; @@ -110,28 +110,29 @@ public LcdDisplay(final Context context, final int sectionIndex, final MidiOut m } setVuMode(states.getVuMode().get()); } - -// private void initSimulation(final MackieMcuProExtension driver, final int sectionIndex, final DisplayPart part) { -// driver.getControllerConfig().getSimulationLayout().layoutDisplay(part, sectionIndex, displayRep); -// for (int i = 0; i < 2; i++) { -// Arrays.fill(lines[i], ' '); -// } -// } - + + // private void initSimulation(final MackieMcuProExtension driver, final int sectionIndex, final DisplayPart + // part) { + // driver.getControllerConfig().getSimulationLayout().layoutDisplay(part, sectionIndex, displayRep); + // for (int i = 0; i < 2; i++) { + // Arrays.fill(lines[i], ' '); + // } + // } + public int getSegmentLength() { return segmentLength; } - + public boolean isLowerDisplay() { return isLowerDisplay; } - + public void setFullTextMode(final int row, final boolean fullTextMode) { this.fullTextMode[row] = fullTextMode; setDisplayBarGraphEnabled(!isFullModeActive()); refreshDisplay(); } - + public void setDisplayBarGraphEnabled(final boolean displayBarGraphEnabled) { if (this.displayBarGraphEnabled == displayBarGraphEnabled) { return; @@ -145,11 +146,11 @@ public void setDisplayBarGraphEnabled(final boolean displayBarGraphEnabled) { switchVuMode(VuMode.LED); } } - + private boolean isFullModeActive() { return fullTextMode[0] | fullTextMode[1]; } - + public void setVuMode(final VuMode mode) { if (hasDedicatedVu) { return; @@ -160,7 +161,7 @@ public void setVuMode(final VuMode mode) { refreshDisplay(); } } - + private void switchVuMode(final VuMode mode) { switch (mode) { case LED: @@ -187,20 +188,20 @@ private void switchVuMode(final VuMode mode) { break; } } - + private void resetGrids(final int row) { Arrays.fill(lastSendGrids[row], " "); } - + public void centerText(final int row, final String text) { sendToDisplay(row, pad4Center(text)); } - + @Override public String toString() { return "LcdDisplay " + part; } - + private String pad4Center(final String text) { final int fill = displayLen - text.length(); if (fill < 0) { @@ -211,7 +212,7 @@ private String pad4Center(final String text) { } return StringUtil.padString(text, fill / 2); } - + public void sendDirect(final String topString, final String bottomString) { lastSentRows[0] = topString; lastSentRows[1] = bottomString; @@ -220,23 +221,23 @@ public void sendDirect(final String topString, final String bottomString) { sendFullRow(0, topString); sendFullRow(1, bottomString); } - + public void sendSegmented(final int row, final List texts) { for (int i = 0; i < 8; i++) { - String text = i < texts.size() ? texts.get(i) : ""; + final String text = i < texts.size() ? texts.get(i) : ""; sendToRowFull(row, i, text); } } - + public void sendToDisplay(final int row, final String text) { -// if (text.equals(lastSentRows[row])) { -// return; -// } + // if (text.equals(lastSentRows[row])) { + // return; + // } lastSentRows[row] = text; resetGrids(row); sendFullRow(row, text); } - + public void sendFullRow(final int row, final String text) { rowDisplayBuffer[6] = (byte) (row * LcdDisplay.ROW2_START); final char[] ca = text.toCharArray(); @@ -246,7 +247,7 @@ public void sendFullRow(final int row, final String text) { displayRep.line(row).text().setValue(text); midiOut.sendSysex(rowDisplayBuffer); } - + public void sendToRow(final int row, final int segment, final String text) { if (row > 1 || row < 0) { return; @@ -256,7 +257,7 @@ public void sendToRow(final int row, final int segment, final String text) { sendTextSeg(row, segment, text); } } - + public void sendToRowFull(final int row, final int segment, final String text) { if (row > 1 || row < 0) { return; @@ -266,7 +267,7 @@ public void sendToRowFull(final int row, final int segment, final String text) { sendTextSegFull(row, segment, text); } } - + private void sendTextSegFull(final int row, final int segment, final String text) { segBuffer[6] = (byte) (row * LcdDisplay.ROW2_START + segment * segmentLength + segmentOffset); final char[] ca = text.toCharArray(); @@ -277,7 +278,7 @@ private void sendTextSegFull(final int row, final int segment, final String text midiOut.sendSysex(segBuffer); displayRep.line(row).text().setValue(String.valueOf(lines[row])); } - + private void sendTextSeg(final int row, final int segment, final String text) { final char[] ca = text.toCharArray(); if (segment == 8) { @@ -297,7 +298,7 @@ private void sendTextSeg(final int row, final int segment, final String text) { displayRep.line(row).text().setValue(String.valueOf(lines[row])); } } - + private void handleLastCell(final int row, final int segment, final char[] ca) { segBufferExp[6] = (byte) (row * LcdDisplay.ROW2_START + segment * segmentLength + segmentOffset); for (int i = 0; i < segmentLength; i++) { @@ -308,7 +309,7 @@ private void handleLastCell(final int row, final int segment, final char[] ca) { } midiOut.sendSysex(segBufferExp); } - + public void refreshDisplay() { for (int row = 0; row < 2; row++) { if (fullTextMode[row]) { @@ -320,27 +321,27 @@ public void refreshDisplay() { } } } - + public void sendChar(final int index, final char cx) { midiOut.sendMidi(Midi.CC, 0x30, cx); } - + public void clearAll() { midiOut.sendSysex(sysHead + "62 f7"); sendToDisplay(0, ""); sendToDisplay(1, ""); } - + public void exitMessage() { midiOut.sendSysex(sysHead + "62 f7"); centerText(topRowFlipped ? 1 : 0, "Bitwig Studio"); centerText(topRowFlipped ? 0 : 1, "... not running ..."); } - + public void clearText() { sendToDisplay(0, ""); sendToDisplay(1, ""); } - - + + } diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/layer/SpecialDeviceModeLayer.java b/src/main/java/com/bitwig/extensions/controllers/mcu/layer/SpecialDeviceModeLayer.java index 251a6346..c30b953f 100644 --- a/src/main/java/com/bitwig/extensions/controllers/mcu/layer/SpecialDeviceModeLayer.java +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/layer/SpecialDeviceModeLayer.java @@ -1,5 +1,6 @@ package com.bitwig.extensions.controllers.mcu.layer; +import com.bitwig.extensions.controllers.mcu.McuExtension; import com.bitwig.extensions.controllers.mcu.VPotMode; import com.bitwig.extensions.controllers.mcu.bindings.ResetableBinding; import com.bitwig.extensions.controllers.mcu.bindings.display.StringRowDisplayBinding; @@ -38,6 +39,7 @@ public SpecialDeviceModeLayer(final Layers layers, final ControlMode mode, final bottomRowInfoText)); device.addUpdateListeners(this::updateBindings); device.addExistChangeListener(this::handleExistenceChanged); + device.getDeviceFollower().getTrackIndex().addListener(this::changeTrackIndex); } private void updateBindings() { @@ -66,6 +68,14 @@ private void resetBindings(final Layer layer) { .forEach(binding -> binding.reset()); } + private void changeTrackIndex(final int index) { + if (active) { + if (device.isSpecificDevicePresent()) { + device.getDeviceFollower().ensurePosition(); + } + } + } + @Override public void assign() { final ControlMode mainMode = !mixer.isFlipped() ? mode : ControlMode.VOLUME; @@ -176,6 +186,10 @@ public void handleInfoState(final boolean start, final Orientation orientation) @Override public void handleModePress(final VPotMode mode, final boolean pressed, final boolean selection) { + McuExtension.println(" Pressed EQ+ %s present=%s", pressed, device.isSpecificDevicePresent()); + if (pressed) { + device.getDeviceFollower().ensurePosition(); + } } } \ No newline at end of file diff --git a/src/main/java/com/bitwig/extensions/controllers/mcu/value/BasicIntValue.java b/src/main/java/com/bitwig/extensions/controllers/mcu/value/BasicIntValue.java new file mode 100644 index 00000000..d2b1ff7b --- /dev/null +++ b/src/main/java/com/bitwig/extensions/controllers/mcu/value/BasicIntValue.java @@ -0,0 +1,30 @@ +package com.bitwig.extensions.controllers.mcu.value; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.IntConsumer; + +public class BasicIntValue { + private int value; + private final List listeners = new ArrayList<>(); + + public void set(final int value) { + if (value != this.value) { + this.value = value; + this.listeners.forEach(listener -> listener.accept(this.value)); + } + } + + public void addListener(final IntConsumer listener) { + this.listeners.add(listener); + } + + public void setImmediately(final int value) { + this.value = value; + } + + public int get() { + return value; + } + +} diff --git a/src/main/resources/Documentation/Controllers/SSL/SSL UF8-UF1.pdf b/src/main/resources/Documentation/Controllers/SSL/SSL UF8-UF1.pdf index 3eff4eae4dcd7573d90ce84eac1149a30441b264..949776e73397d6126bee598fc997b3e33d44e61e 100644 GIT binary patch delta 11873 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

hF1$p!`oxSE0^ zOR%K@*)&+$hk1lxFgC!_i|b(A1BNW*N?!G1fTob(pgSdar_|Sr0%SpwFGN z1Jck*HZuqIXCiB22=LILV)p>Akt>_SAcs4mIY#((01Vh;88DS;DE!Wmz%pki~%mJi@hecY7zC=F!rr^#5A_hCR{(hdZ4XUUFEbyp^h%h%c#$#R{> zRHIC_5PF4ErJMTpXJm=H?g?iA5t9HIFvl{Kk$KqS!Au|06+U~0g5zNZc`YO%M*m;k z%n&IiFMzVxJ4m#oi-g4AX+y(x0BqT+)n;Y&@{?Mrxiy2FWvmLHE@)h<#>nlqVaPZMUndufzXwT2g^T#=uwK-OndrHum_Yl=0h?LGuw;jpLVMab8pORT6=8$`pkNL=Y4I9`m+y%!QI{q+dLS-0!NuvfQ zF${-pw*4_i7DIN04uQZY8ySngEC8LuWJ5IMUH~tT2$!=qRHhJNu$v@00X|t>sZndB zk?PpccJxI2cJ7&?av;?es?Q?MakOO7;;BM9G4YQUQd+MP5&Co(mb2{*rSmQU+2Rtl z5*?k{&4{y3vj9?tqax<;rfU_CQJ#D|vk&OWAH51}>Jh0uyTWXHyiMUHwlT!INj^Yn zlId*sv{gkcNrM~m+$(L)W8Ce|d*A{_#ZCe9MaW?y)&xrzcL=CL1lTt6sDA91@zogE zaTbCQnu?THd8j3K(iB~LsTreuUwPGV?9PBh@=U8?)bf6}!=CDFz7(u+>lAxmME=rEHuOW&>c-{%@AxT{NfeycqW(q8Qf>=( z+x`NU2kr#{G8>-&wG%K_K1SW4zftV4&(y$>R4(cl@9K9)M9*#cq}6DRZXuOhi~D)d zO?-sDpw9Lup`6T;BF^tLS2c{~P+K+bdUT+IYiiXPWk67X*GncJ-qq}lCkcZkL!-|5 z!t0+{E@rajU-p~*HbEvc^6QrE-e9SXzXWlU7x0)@LnvObhyw1fER}GYxegZmpS#f^ zf^ho%==ELSF3W~c!UxfV1NZ<&IBXWZ=AB%Q8jeax^tJ@s%cT#E%@DVso=mbc%(Q%4 zk(+b34;$FieGTpvSj`iQNA*vg-uiYL>*^M+ncx179%Cla%SF^v0EE zL)kz5%_J{hb+lWdMQ{6C-};n8Qry}tHzTMRg$C?)mOov z!)N-@3GwgT7=5y-no=kA2Xi7V=u6GgeoOQ*0f0ok34FzY>B%(**93TRfZgOSkQRCI zhMIbccp9wRe^*|&RXw(*m+pM7m8M2JHwQ-@@W@7RsScMt1trdyYX3p%!uJ!X}< zamrRi`a60pxb8_jom#93elMp}q@qB?q>gqwO!Fp3{aFS4`M=nGo4o?l7bz_WB2#73 zlnT#11i#PKQa5ob$;MwIv0b+zKA$K!F(ei#?l=XzFempnTS#-(L-z?U%S1cSCM`Bw zDkfX~UDMbvSDz>_rvFg2d<=W#wKXDzk*2U80Atgp^`cX9|FOyv+%YvYqRAKu#vN=8 zpY`<`T+9Td^wlxD5;URu`q192G@s%!><=~_n!IHhO%Fwj^ly&x`qJ7#0!ey)P3Q?T zzdD^ZGf+7(%g-)G(#Pj4*PE$7NXDkhD4?!a@>yMs&JJ^mc!qJtwJY{syWKs*{}dGN z8ugdX>0*Jik>-gb22Zh&+X2y=i4HSe>OR#?z!z`bu$@8HW@J|`bbcY`cHI$ zY=l^=XG->0P$rl8q8Ya!o}w=gf@2Fv^UTvpAcqA0;)na!7p70`5~tzJYs^kngzK3> z)dGyKt!dEOB)-rK92S9%QR#)2OdY5Ak3Ohq%l`O?_GSc{qOh#q&ius=A=iWCw$vX# zDRv01H7!!kF_+u)7p&0T|7$x}TG3N)E?zt@R-G%4x4XD<6moNi1Rb7ahH3TxPEaZJ z8WtI^+e{kZ>d5{nm+ySuQR}7#wM}l!oy;=b-JG^H{S+ti5Nlq^7)2%hI-z$?4+2*} zCiQLS%wKEMijTD*akH&VJ_e>dAmI$E`9j-WCLKujcG=T_>_@K6_WWo6{{ z;>Ly4f}3YZ&+R&7YJ}V>D>2;d1)`mWL{4)$0E4fus?sQ5=e^K@;OVv$2WeWekk?d; zlUpcM2txmCY$q@o(@s4!EA`db&#uIM$Kq$7GExDtemM<>rWYopbE@r%)m zXtx>s6_LU+u(j4;RR#TgW{v;q0jEyZv5L@P4xXQjho66&f?qXSGuUE^isy|*>9%5K zWvA;266qljeY-VT@a64%T|~WY=8nACH+3GY;j_*&Vf|P|T*hsG_RHW@^}ZbOukx)@ z=gx_`&n7BT+wco(hMbj6TAs?gE#>CnP3uy5x20O|wsZ)6ozCi8E?NievJ(;WM$uoq zTgx1QHrOW#<4=!M+%<{*mQS*ka5(W%CI3W}oI`iUR`gtpXORMXFD4R3DIqTj*P72; zkEpAV*W7`DYkUOh8Jpd}V>ih4tr(--Rxpu>f?s-2tA1@4-}{An)#0FvkD|p#{Kf zPgBd&(VP*JMuT03ntnJa&-S`myPc^8nzAycV&6JGJ|?H5w{K2GaZSquYHX>|b!NmP z^tu#v#tm|w`O*UYxgwvEnX5)(a{l2!1!69xbu5F?^SP>c&Fe-}0+I5YXd=4Ukr|7t zOqP#Rwj|sA$NE@?^Fj{p5nH@PzEPaaPZoygC;SPmu%IlJrRc`woSia08R}DUvgQt6 zDJxMiH6{+r%Wmh09wMn8GJ;(dGCrR2qTk6>UR7DdAyIjOP*i0alsFpDIA>avDCF0d zgy^few0mtWjd42Vtfa9TD-PIlqxlF`Nh)62*XYJ6ac4;it6<|R3fepZwFHhb;{-7N z|Gasb7I4Ols`ZYs9yL#?Va!E8)yyzQ&6rCm!j6_)u)ZyQmEJchyn!Ygx&9<%4fxiP zg3nk+3A4y$N`20hkT@=RRv*JB28mKk@MnZym6)SyNb_@HLP*1OQON{I*C%C9-7JR| zo3M8h7O1i`C}A|90nRj(?q5Ge15h>T&xc$~uKo{UBnTCL3l}n<#Oq#46B(roI>Pfx zl>+lW;}O<-8n*Nsq8A!sMmsaa(eczJLNc{RzBaX}6;s$eWYJ(c$Erjh{h&1+prXdd zWJ)8*%G4hjXWCWIt)J_SSSL{uH7Bqe2-o-%7RjE-s31#Sk%U-^GEj0*}Aja|MiqJ^UnrZ@MsS4%dcTy5y} zS*5;iHqv1;evUy3zh< z9*{gNeZ;)LO*eLqW->vO>cNXn6sq@9n&dlbDY`ejvBNZeNze9LN&7WgY;G+zGgO8@ z*%sEf+w~*GKE}w9a2!JCs8#Z)nfk)S4nYAU5`p8PZD671x8vBqeu$l4rybL+WeZ81j58!mpKipEX}5})pQJT zs&>b2kc6GbmjNK0J8^YKArICM4r=X`O9qFMeqzi(LXR4a6@uU_?#)Jh;EuBNae`qD z5Q~T(C=U_MyANbjG?3f0pAC$2ns#yCTdjvK&78K)ZCb-EfKD_Q>ZvB##G<<4fr_|Q z=qEQ=pi==VF-0xijYfiQSdom8@52Nzr&@P%+hFPV`5Bv@ItIg-k;lrZyTNMwgJCsn<@ALxgGj+80e@utn(O^kq!T7tSiS)9 zhm44}8GDCnA|bx)K=SaQp5V{d8mq|1pUXe3BCgimJagj}tfhwJ!ZrH!$Dp-)Fg7^y zy$YAE9z)Lh*0UowLUV-zI=aN<^b$H$pg}lBRRV$WnawUv%be>Wk>>%EJ~)=-$1ie; zLZzer$vwpx+*YT*tN5e*%y%iJ^5_-{qy`<@dMV1#TfVW!Do*ODw9@#c=NPDV{Y%Ut zFWxsOUtioC)ycPNW3IYka7+)YEwZoOTC2^O*M`jP!%K7XoLJjoDl6BH%L>qXuyqKe zNJ4kB1y>wkohr$%e{c#~+Y;_#Ag)fu8m(n;qqKc`2tqnXGK)^;kCZ1)q~BjfuP5g^ zB%Q6O*`}3?z|ubmV=kFd7t}^|>-zc7w$mDMaVUW8Z?I*rTl63ztiv)hW0!VVTu*YH zS@Uj2Aiw-Y3V-?#^LE`Lc&4_Kq6v?%`13a3jIsHry2f2`L;8XgzlMHr5Odhq5GV2< zXQ(s7yPp}&{u?jd@cd2q%XC)XQvhEjJd~zJsdI=<>zO8k9Lq&<^U96bVVRsHuGGNg zGx+-4!#X%wSQfG09^vi{>x0Wfxa&EFeikIB!c+s~HLIFO#X93HZ(Fl0)>iFGY&BJC z!my{+x<@iU zU+d1uukzmz?nU!g7&XlDP~Km2_nRmix1%9iD{ryhz3J75kUzcp`UUVeq-pa_o<)_Bf zwvL}OA{+JtTbPA*4G=puNDw!*Z)Zx5h~5`l`|0%}i?!~e!KZ)un7t>ByJ{F~DrZ>> zK{^<+t&aV?KOwx^|APN?pjY0no&kk)!*34#k~?DtHX*RP2OrAP%5>iS5r~aD?6(f^ z9oX;q_zs+R;JyRz9r*7ccn4wAZyll)03r`Nh>eSbJ*~o!h~)nbnkEg2O3ta-z#u+$ zDRE{lUP&=#NilISGp{6@6!ZH*NltNIHa;;)p0o@lVs2^)HXa^vP6=i{PDvhSu#_Y> zGoKh6A2W{x8!wnsf`?OrLn`fBiMRlTN0lAK&&I|7e!W~}VuAk|?OdL`W#&FAmh+0IFM#aJ09GPL&7jrF-couCcbxFYtiXFm&;_Av3L%DRVPoXoq z+6>S%-Bv@b9S(xY_FWGCq4s?_n;zc{$cS9e4+?87C0)K=4q|bpWd<>}DaM7#)t$2??O!#@V zsS$wYKb2OH;l+WojC%k}oRfj5trBR11RzfLqHYW6`IBXRm%&YjRUN@txiWfe-;;}* zklkb~=19T%F{$dP_ZXKGM$glcj*p;ac`9!lV&L6v%woUwU`Ioc8ORz}=geHI$J9o~5L{AXF(fxN@-<_>be%!2>8eyQ$~ z)^S99wl(MPL6);VcD!TG-uyN@4L2Shcv5t@_UCY5qpxne=v77Uv?E*B-zrL@P8pOl z#uD|`oYRnWXMXPHwDuyLY1q0ad!(q7r|X^F=0{Z#W(E9hZ`ku3-}^I$=&}8oCzLo{!!#MLmZtQv zW%C}tHQECqhuG(^Pk&$BjRrR1Hu%OZw}~ExZ}(XQZ#>%0@#WE zPSzIqJd&i4Nc$jW9$wHvvp_eTL>kDs)th8%ZH^(J$WG82g#Fh8`HdsZVi%rD%b&E_F(q9`)3HqScHyV^ zp;$#~v{0S#!XRXTZsE(mf8g`fY61d7w;<9(73id<^z zLQ1(GZn8iXvNF_V@lP;*Qd7n3As8X(A)z7r$-fzr>Cxz!gdRW1Hp%6ua&X<=h}P6R zHg>xOs%Hx0Y@}@=kGr(0Pf5^;TgJv<9*!upeV|M=!RRU}{T}MnYHynHB}!BSQ+#D( zF@9zWIV_CnP&VP#K^OSloGLZAL2^^W7-9?XJ}ZeX%)a7DWKlDyGDt4e+NK%I5$zKJj!0 z=2*P#tPJFS{+)Vx252V*GIX!frFr;*-lV6 zchG~pgQxM=b!Jq#;qy&r>kkO(QVENjF}tVs)eF~Ag+BLulyXPE|IdgdZ^2T_Pls1E ze}tto*pFIj`w*>C+`m>jXZ}U#%7z%@&g0nB5D#cqF><#pAf3+t4PAY(*~u_2blmNxQxdHdjvLIYCo{W4VCLpc?3pYq5p|S@Rm`z%MGEBM5uT zVgaZtF@aQLZru(sT|?Mu7>B!$T-f^sx$o!1ue3`AVjMJX3N{J{V`~(4Zg$=@7hPf_ zF-b5O%r3>j#wo$aDaOIW2?B9(Npi9CNr2fUxOqSv!h-)dMjDCuv;lYHMmoSC`*eDB*l_7rdqEVSfc z`Lso~rq;Iva7`dEN<@xP#zcF5gP25gUkQm$FBtCf7J*IFt@sAN*r`5P_lCn>x1Pv?0MqovCs4B`9&w6Ixh zpDnvgC5&?KFgWXZ)LwP`4d;b&os(XZG~-{nwL25cC*N3| z!y7^!W$hroXf2pL4!AdW?@tcOVsA9kxxhub<~sjG2|B{2Q}8%}!eekXvuhNV<0f*A z3}$R;3I^pm5Ut5q)lLggt`B&HnGC1hzs%HjM{j57xo<^Oe#-SD6zeABt}sqoTE|_a tEmWYIkzef>QYEbTf?{TG`u~*qeeZonrAZqQW1?_z@S)JsN-9dB{4c9js+a%( delta 11526 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 B@S^|#