From f3b21b395ef7df0d44804c5b261db9f7124d146c Mon Sep 17 00:00:00 2001 From: Vincenzo Sicurella Date: Sun, 28 Jul 2024 22:18:37 -0400 Subject: [PATCH] refactor some low level controller methods, add import status structs, add wip lastSavedLayout to editor state --- Source/LumatoneEditorState.cpp | 12 ++- Source/LumatoneEditorState.h | 4 +- Source/Main.cpp | 2 +- Source/MidiEditArea.cpp | 2 +- .../data/application_state.cpp | 59 +++++++++---- .../data/application_state.h | 25 +++--- .../device/lumatone_controller.cpp | 10 +-- .../device/lumatone_controller.h | 4 +- .../device/lumatone_event_manager.cpp | 87 +++++++++++++++---- 9 files changed, 152 insertions(+), 53 deletions(-) diff --git a/Source/LumatoneEditorState.cpp b/Source/LumatoneEditorState.cpp index a729994..8deb92d 100644 --- a/Source/LumatoneEditorState.cpp +++ b/Source/LumatoneEditorState.cpp @@ -60,6 +60,8 @@ LumatoneEditorState::LumatoneEditorState(juce::ValueTree stateIn, LumatoneFirmwa loadPropertiesFile(nullptr); + lastSavedLayout = std::make_shared(); + colourPalettes = std::make_shared>(); colourSelectionGroup = std::make_shared("LumatoneEditorAppColourGroup"); } @@ -437,7 +439,7 @@ bool LumatoneEditorState::Controller::performAction(LumatoneAction *action, bool { if (LumatoneApplicationState::Controller::performAction(action, undoable, newTransaction)) { - setHasChangesToSave(true); + return true; } @@ -452,6 +454,7 @@ bool LumatoneEditorState::Controller::resetToCurrentFile() // Replace with blank file LumatoneLayout defaultLayout; editorState.setCompleteConfig(defaultLayout); + *editorState.lastSavedLayout = defaultLayout; editorState.setHasChangesToSave(false); return true; } @@ -474,6 +477,9 @@ bool LumatoneEditorState::Controller::resetToCurrentFile() // Send configuration to controller, if connected editorState.setCompleteConfig(keyMapping); + // Save copy that doesn't get updated + *editorState.lastSavedLayout = keyMapping; + // Clear undo history editorState.undoManager->clearUndoHistory(); @@ -506,11 +512,11 @@ bool LumatoneEditorState::Controller::openRecentFile(int recentFileIndex) return setCurrentFile(editorState.recentFiles->getFile(recentFileIndex)); } -bool LumatoneEditorState::Controller::requestCompleteConfigFromDevice() +bool LumatoneEditorState::Controller::requestCompleteDeviceConfig() { setHasChangesToSave(false); editorState.undoManager->clearUndoHistory(); - return LumatoneApplicationState::Controller::requestCompleteConfigFromDevice(); + return LumatoneApplicationState::Controller::requestCompleteDeviceConfig(); } bool LumatoneEditorState::Controller::saveMappingToFile(juce::File fileToSave) diff --git a/Source/LumatoneEditorState.h b/Source/LumatoneEditorState.h index e7f4b28..19a4e2f 100644 --- a/Source/LumatoneEditorState.h +++ b/Source/LumatoneEditorState.h @@ -174,6 +174,8 @@ class LumatoneEditorState : public LumatoneApplicationState std::shared_ptr propertiesFile; + std::shared_ptr lastSavedLayout; + protected: // Global UI constants @@ -205,7 +207,7 @@ class LumatoneEditorState : public LumatoneApplicationState bool resetToCurrentFile(); bool openRecentFile(int recentFileIndex); - virtual bool requestCompleteConfigFromDevice() override; + virtual bool requestCompleteDeviceConfig() override; void addPalette(const LumatoneEditorColourPalette& newPalette); bool deletePaletteFile(juce::File pathToPalette); diff --git a/Source/Main.cpp b/Source/Main.cpp index 8e6460e..c7c7263 100644 --- a/Source/Main.cpp +++ b/Source/Main.cpp @@ -686,7 +686,7 @@ bool TerpstraSysExApplication::onRequestDeviceConfig() } // resetSysExMapping(); - requestCompleteConfigFromDevice(); + requestCompleteDeviceConfig(); return true; } diff --git a/Source/MidiEditArea.cpp b/Source/MidiEditArea.cpp index 217ad12..796ad4e 100644 --- a/Source/MidiEditArea.cpp +++ b/Source/MidiEditArea.cpp @@ -530,7 +530,7 @@ void MidiEditArea::onOpenConnectionToDevice(juce::String dialogTitle) if (retc == 0) // Import { // TODO non getLumatoneController call - LumatoneEditorState::Controller::requestCompleteConfigFromDevice(); + LumatoneEditorState::Controller::requestCompleteDeviceConfig(); setEditMode(EditorMode::ONLINE); } else if (retc == 1) // Send diff --git a/Source/lumatone_editor_library/data/application_state.cpp b/Source/lumatone_editor_library/data/application_state.cpp index 12b1b85..5ace219 100644 --- a/Source/lumatone_editor_library/data/application_state.cpp +++ b/Source/lumatone_editor_library/data/application_state.cpp @@ -1,4 +1,5 @@ #include "application_state.h" + #include "../device/lumatone_controller.h" #include "../device/activity_monitor.h" #include "../color/colour_model.h" @@ -11,6 +12,7 @@ #include "../listeners/midi_listener.h" #include "../lumatone_midi_driver/lumatone_midi_driver.h" +#include "../lumatone_midi_driver/firmware_types.h" juce::Array getLumatoneApplicationProperties() { @@ -36,6 +38,9 @@ LumatoneApplicationState::LumatoneApplicationState(juce::ValueTree stateIn, Luma selectedKeys = std::make_shared>(); + receiveSettingsStatus = std::make_shared(); + receiveLayoutStatus = std::make_shared(); + loadStateProperties(stateIn); } @@ -51,6 +56,8 @@ LumatoneApplicationState::LumatoneApplicationState(juce::String nameIn, const Lu , activityMonitor(stateIn.activityMonitor) , colourModel(stateIn.colourModel) , selectedKeys(stateIn.selectedKeys) + , receiveSettingsStatus(stateIn.receiveSettingsStatus) + , receiveLayoutStatus(stateIn.receiveLayoutStatus) { loadStateProperties(state); } @@ -389,7 +396,7 @@ void LumatoneApplicationState::setInvertSustain(bool invert) if (doSendChangesToDevice()) { - controller->invertSustainPedal(invert); + controller->setInvertSustainPedal(invert); } editorListeners->call(&LumatoneEditor::EditorListener::invertSustainToggled, invert); @@ -561,6 +568,16 @@ void LumatoneApplicationState::Controller::updatedSelectedKeys() appState.editorListeners->call(&LumatoneEditor::EditorListener::selectionChanged); } +FirmwareSupport::ReceiveSettingsStatus &LumatoneApplicationState::Controller::getReceivedSettingsStatus() +{ + return *appState.receiveSettingsStatus; +} + +FirmwareSupport::ReceiveLayoutStatus &LumatoneApplicationState::Controller::getReceivedLayoutStatus() +{ + return *appState.receiveLayoutStatus; +} + bool LumatoneApplicationState::Controller::performAction(LumatoneAction *action, bool undoable, bool newTransaction) { return appState.performLumatoneAction(action, undoable, newTransaction); @@ -606,45 +623,57 @@ void LumatoneApplicationState::removeMidiListener(LumatoneEditor::MidiListener* midiListeners->remove(listenerIn); } -bool LumatoneApplicationState::Controller::requestCompleteConfigFromDevice() +bool LumatoneApplicationState::Controller::requestCompleteDeviceConfig() { if (appState.connectionState != ConnectionState::ONLINE) return false; - requestSettingsFromDevice(); - requestMappingFromDevice(); + requestDeviceGlobalSettings(); + requestDeviceMapping(); return true; } -bool LumatoneApplicationState::Controller::requestSettingsFromDevice() +bool LumatoneApplicationState::Controller::requestDeviceGlobalSettings() { if (appState.connectionState != ConnectionState::ONLINE) return false; + // Reset state for tracking response progress + *appState.receiveSettingsStatus = FirmwareSupport::ReceiveSettingsStatus(appState.getLumatoneVersion()); + + // Velocity curve config + appState.controller->sendVelocityIntervalConfigRequest(); + // Macro button colours appState.controller->requestMacroButtonColours(); // General options - appState.controller->requestPresetFlags(); - appState.controller->requestExpressionPedalSensitivity(); - - // Velocity curve config - appState.controller->sendVelocityIntervalConfigRequest(); - appState.controller->sendVelocityConfigRequest(); - appState.controller->sendFaderConfigRequest(); - appState.controller->sendAftertouchConfigRequest(); + appState.controller->getPeripheralChannels(); return true; } -bool LumatoneApplicationState::Controller::requestMappingFromDevice() +bool LumatoneApplicationState::Controller::requestDeviceMapping() { if (appState.connectionState != ConnectionState::ONLINE) return false; + // Reset state for tracking response progress + *appState.receiveLayoutStatus = FirmwareSupport::ReceiveLayoutStatus(appState.getLumatoneVersion(), appState.getNumBoards()); + // Request MIDI channel, MIDI note, colour and key type config for all keys - appState.controller->sendGetCompleteMappingRequest(); + appState.controller->sendGetAllBoardsMappingRequest(); + + // Request mapping look-up tables + appState.controller->sendVelocityConfigRequest(); + appState.controller->sendFaderConfigRequest(); + appState.controller->sendAftertouchConfigRequest(); + + // Request settings associated with mapping + appState.controller->requestExpressionPedalSensitivity(); + appState.controller->requestPresetFlags(); + return true; } diff --git a/Source/lumatone_editor_library/data/application_state.h b/Source/lumatone_editor_library/data/application_state.h index fe925e5..ed5f55e 100644 --- a/Source/lumatone_editor_library/data/application_state.h +++ b/Source/lumatone_editor_library/data/application_state.h @@ -164,6 +164,9 @@ class LumatoneApplicationState : public LumatoneState std::shared_ptr activityMonitor; std::shared_ptr colourModel; + std::shared_ptr receiveSettingsStatus; + std::shared_ptr receiveLayoutStatus; + bool contextIsSet = false; //================================================================================ @@ -174,24 +177,26 @@ class LumatoneApplicationState : public LumatoneState Controller(LumatoneApplicationState& stateIn) : appState(stateIn) {} - virtual bool requestSettingsFromDevice(); - virtual bool requestMappingFromDevice(); - virtual bool requestCompleteConfigFromDevice(); + virtual bool requestDeviceGlobalSettings(); + virtual bool requestDeviceMapping(); + virtual bool requestCompleteDeviceConfig(); - void setInactiveMacroButtonColour(juce::Colour buttonColour); - void setActiveMacroButtonColour(juce::Colour buttonColour); + void setInactiveMacroButtonColour(juce::Colour buttonColour); + void setActiveMacroButtonColour(juce::Colour buttonColour); - void setSelectedKeys(juce::Array selection); - void addSelectedKey(int keyNum); - void removeSelectedKey(int keyNum); + void setSelectedKeys(juce::Array selection); + void addSelectedKey(int keyNum); + void removeSelectedKey(int keyNum); protected: + void updatedSelectedKeys(); - void updatedSelectedKeys(); + FirmwareSupport::ReceiveSettingsStatus& getReceivedSettingsStatus(); + FirmwareSupport::ReceiveLayoutStatus& getReceivedLayoutStatus(); public: - virtual bool performAction(LumatoneAction* action, bool undoable=true, bool newTransaction=true); + virtual bool performAction(LumatoneAction* action, bool undoable=true, bool newTransaction=true); protected: juce::ListenerList* getEditorListeners() const { return appState.editorListeners.get(); } diff --git a/Source/lumatone_editor_library/device/lumatone_controller.cpp b/Source/lumatone_editor_library/device/lumatone_controller.cpp index b68d080..d7ef082 100644 --- a/Source/lumatone_editor_library/device/lumatone_controller.cpp +++ b/Source/lumatone_editor_library/device/lumatone_controller.cpp @@ -129,7 +129,7 @@ void LumatoneController::sendCurrentCompleteConfig(bool signalEditorListeners) sendLightOnKeyStrokes(getMappingData()->getLightOnKeyStrokes()); sendInvertFootController(getMappingData()->getInvertExpression()); sendExpressionPedalSensivity(getMappingData()->getExpressionSensitivity()); - invertSustainPedal(getMappingData()->getInvertSustain()); + setInvertSustainPedal(getMappingData()->getInvertSustain()); // Velocity curve config setVelocityIntervalConfig(getMappingData()->getConfigTable(LumatoneConfigTable::velocityInterval)->velocityValues); @@ -146,7 +146,7 @@ void LumatoneController::sendGetMappingOfBoardRequest(int boardId) getFaderTypeConfig(boardId); } -void LumatoneController::sendGetCompleteMappingRequest() +void LumatoneController::sendGetAllBoardsMappingRequest() { for (int boardId = 1; boardId <= getNumBoards(); boardId++) sendGetMappingOfBoardRequest(boardId); @@ -520,7 +520,7 @@ void LumatoneController::getPeripheralChannels() firmwareDriver.getPeripheralChannels(); } -void LumatoneController::invertSustainPedal(bool setInverted) +void LumatoneController::setInvertSustainPedal(bool setInverted) { LumatoneState::setInvertSustain(setInverted); @@ -571,7 +571,7 @@ void LumatoneController::onConnectionConfirmed() { checkingDeviceIsLumatone = false; currentDevicePairConfirmed = true; - + if (getSerialNumber().isEmpty()) { waitingForFirmwareVersion = true; @@ -613,7 +613,7 @@ void LumatoneController::serialIdentityReceived(const int* serialBytes) sendGetFirmwareRevisionRequest(); else onConnectionConfirmed(); - + setConnectionState(ConnectionState::ONLINE); } diff --git a/Source/lumatone_editor_library/device/lumatone_controller.h b/Source/lumatone_editor_library/device/lumatone_controller.h index 42156eb..53f7cd0 100644 --- a/Source/lumatone_editor_library/device/lumatone_controller.h +++ b/Source/lumatone_editor_library/device/lumatone_controller.h @@ -72,7 +72,7 @@ class LumatoneController : private LumatoneApplicationState void sendGetMappingOfBoardRequest(int boardId); // Send request to receive the complete current mapping on the controller - void sendGetCompleteMappingRequest(); + void sendGetAllBoardsMappingRequest(); // Send parametrization of one key to the device void sendKeyParam(int boardId, int keyIndex, LumatoneKey keyData, bool signalEditorListeners=true, bool bufferKeyUpdates=false); @@ -189,7 +189,7 @@ class LumatoneController : private LumatoneApplicationState void getPeripheralChannels(); // Invert the polarity of the sustain pedal - void invertSustainPedal(bool setInverted); + void setInvertSustainPedal(bool setInverted); // Reset preset mappings to factory mappings void resetPresetsToFactoryDefault(); diff --git a/Source/lumatone_editor_library/device/lumatone_event_manager.cpp b/Source/lumatone_editor_library/device/lumatone_event_manager.cpp index 5b4c076..e41635f 100644 --- a/Source/lumatone_editor_library/device/lumatone_event_manager.cpp +++ b/Source/lumatone_editor_library/device/lumatone_event_manager.cpp @@ -134,8 +134,25 @@ FirmwareSupport::Error LumatoneEventManager::handleLEDConfigResponse(const juce: if (errorCode == FirmwareSupport::Error::noError) { - int colorCode = cmd - GET_RED_LED_CONFIG; - firmwareListeners.call(&LumatoneEditor::FirmwareListener::octaveColourConfigReceived, boardId, colorCode, colourData); + int colourCode = cmd - GET_RED_LED_CONFIG; + + switch (colourCode) + { + default: + jassertfalse; + break; + case 0: + getReceivedLayoutStatus().numRedConfigReceived++; + break; + case 1: + getReceivedLayoutStatus().numGreenConfigReceived++; + break; + case 2: + getReceivedLayoutStatus().numBlueConfigReceived++; + break; + } + + firmwareListeners.call(&LumatoneEditor::FirmwareListener::octaveColourConfigReceived, boardId, colourCode, colourData); } return errorCode; @@ -150,7 +167,11 @@ FirmwareSupport::Error LumatoneEventManager::handleChannelConfigResponse(const j data[i]++; // MIDI Channels are 1-based return errorCode; }; - auto callback = [&](int boardId, void* data) { firmwareListeners.call(&LumatoneEditor::FirmwareListener::octaveChannelConfigReceived, boardId, (int*)data); }; + auto callback = [&](int boardId, void* data) + { + getReceivedLayoutStatus().numChannelConfigReceived++; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::octaveChannelConfigReceived, boardId, (int*)data); + }; return handleOctaveConfigResponse(midiMessage, unpack, callback); } @@ -159,7 +180,11 @@ FirmwareSupport::Error LumatoneEventManager::handleNoteConfigResponse(const juce auto unpack = [&](const juce::MidiMessage& msg, int& boardId, juce::uint8 numKeys, int* data) { return LumatoneSysEx::unpackGetNoteConfigResponse(msg, boardId, numKeys, data); }; - auto callback = [&](int boardId, void* data) { firmwareListeners.call(&LumatoneEditor::FirmwareListener::octaveNoteConfigReceived, boardId, (int*)data); }; + auto callback = [&](int boardId, void* data) + { + getReceivedLayoutStatus().numNoteConfigReceived++; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::octaveNoteConfigReceived, boardId, (int*)data); + }; return handleOctaveConfigResponse(midiMessage, unpack, callback); } @@ -168,7 +193,11 @@ FirmwareSupport::Error LumatoneEventManager::handleKeyTypeConfigResponse(const j auto unpack = [&](const juce::MidiMessage& msg, int& boardId, juce::uint8 numKeys, int* data) { return LumatoneSysEx::unpackGetTypeConfigResponse(msg, boardId, numKeys, data); }; - auto callback = [&](int boardId, void* data) { firmwareListeners.call(&LumatoneEditor::FirmwareListener::keyTypeConfigReceived, boardId, (int*)data); }; + auto callback = [&](int boardId, void* data) + { + getReceivedLayoutStatus().numKeyTypeConfigReceived++; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::keyTypeConfigReceived, boardId, (int*)data); + }; return handleOctaveConfigResponse(midiMessage, unpack, callback); } @@ -177,7 +206,11 @@ FirmwareSupport::Error LumatoneEventManager::handleVelocityConfigResponse(const auto unpack = [&](const juce::MidiMessage& msg, int* data) { return LumatoneSysEx::unpackGetVelocityConfigResponse(msg, data); }; - auto callback = [&](void* data) { firmwareListeners.call(&LumatoneEditor::FirmwareListener::velocityConfigReceived, (int*)data); }; + auto callback = [&](void* data) + { + getReceivedLayoutStatus().receivedVelocityTable = true; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::velocityConfigReceived, (int*)data); + }; return handleTableConfigResponse(midiMessage, unpack, callback); } @@ -186,7 +219,11 @@ FirmwareSupport::Error LumatoneEventManager::handleAftertouchConfigResponse(cons auto unpack = [&](const juce::MidiMessage& msg, int* data) { return LumatoneSysEx::unpackGetAftertouchConfigResponse(msg, data); }; - auto callback = [&](void* data) { firmwareListeners.call(&LumatoneEditor::FirmwareListener::aftertouchConfigReceived, (int*)data); }; + auto callback = [&](void* data) + { + getReceivedLayoutStatus().receivedAftertouchTable = true; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::aftertouchConfigReceived, (int*)data); + }; return handleTableConfigResponse(midiMessage, unpack, callback); } @@ -195,7 +232,11 @@ FirmwareSupport::Error LumatoneEventManager::handleVelocityIntervalConfigRespons auto unpack = [&](const juce::MidiMessage& msg, int* data) { return LumatoneSysEx::unpackGetVelocityIntervalConfigResponse(msg, data); }; - auto callback = [&](void* data) { firmwareListeners.call(&LumatoneEditor::FirmwareListener::velocityIntervalConfigReceived, (int*)data); }; + auto callback = [&](void* data) + { + getReceivedSettingsStatus().receivedVelocityIntervalTable = true; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::velocityIntervalConfigReceived, (int*)data); + }; return handleTableConfigResponse(midiMessage, unpack, callback); } @@ -204,7 +245,11 @@ FirmwareSupport::Error LumatoneEventManager::handleFaderConfigResponse(const juc auto unpack = [&](const juce::MidiMessage& msg, int* data) { return LumatoneSysEx::unpackGetFaderConfigResponse(msg, data); }; - auto callback = [&](void* data) { firmwareListeners.call(&LumatoneEditor::FirmwareListener::faderConfigReceived, (int*)data); }; + auto callback = [&](void* data) + { + getReceivedLayoutStatus().receivedFaderTable = true; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::faderConfigReceived, (int*)data); + }; return handleTableConfigResponse(midiMessage, unpack, callback); } @@ -213,7 +258,11 @@ FirmwareSupport::Error LumatoneEventManager::handleFaderTypeConfigResponse(const auto unpack = [&](const juce::MidiMessage& msg, int& boardId, juce::uint8 numKeys, int* data) { return LumatoneSysEx::unpackGetTypeConfigResponse(msg, boardId, numKeys, data); }; - auto callback = [&](int boardId, void* data) { firmwareListeners.call(&LumatoneEditor::FirmwareListener::faderTypeConfigReceived, boardId, (int*)data); }; + auto callback = [&](int boardId, void* data) + { + getReceivedLayoutStatus().numFaderTypeConfigReceived++; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::faderTypeConfigReceived, boardId, (int*)data); + }; return handleOctaveConfigResponse(midiMessage, unpack, callback); } @@ -288,6 +337,8 @@ FirmwareSupport::Error LumatoneEventManager::handleGetPeripheralChannelResponse( channelSettings.sustainPedal ); + getReceivedSettingsStatus().receivedPeripheralChannels = true; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::peripheralMidiChannelsReceived, channelSettings); return errorCode; @@ -308,6 +359,8 @@ FirmwareSupport::Error LumatoneEventManager::handleGetPresetFlagsResponse(const mappingData->setInvertExpression(presetFlags.expressionPedalInverted); mappingData->setInvertSustain(presetFlags.sustainPedalInverted); + getReceivedLayoutStatus().receivedPresetFlags = true; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::presetFlagsReceived, presetFlags); return errorCode; @@ -320,6 +373,8 @@ FirmwareSupport::Error LumatoneEventManager::handleGetExpressionPedalSensitivity mappingData->setExpressionSensitivity(sensitivity); + getReceivedLayoutStatus().receivedExprPedalSensitivity = true; + firmwareListeners.call(&LumatoneEditor::FirmwareListener::expressionPedalSensitivityReceived, sensitivity); return errorCode; @@ -335,9 +390,11 @@ FirmwareSupport::Error LumatoneEventManager::handleGetMacroLightIntensityRespons return errorCode; LumatoneState::setActiveMacroButtonColour(activeColour); - getEditorListeners()->call(&LumatoneEditor::EditorListener::macroButtonActiveColourChanged, activeColour); - LumatoneState::setInactiveMacroButtonColour(inactiveColour); + + getReceivedSettingsStatus().receivedMacroButtonColours = true; + + getEditorListeners()->call(&LumatoneEditor::EditorListener::macroButtonActiveColourChanged, activeColour); getEditorListeners()->call(&LumatoneEditor::EditorListener::macroButtonInactiveColourChanged, inactiveColour); return FirmwareSupport::Error::noError; @@ -569,8 +626,8 @@ void LumatoneEventManager::timerCallback() auto cmd = sysExData[CMD_ID]; configMsg = cmd < 0x2; - - + + #if JUCE_DEBUG if (verbose > 1) DBG("READ: " + midiMessage.getDescription()); @@ -697,7 +754,7 @@ void LumatoneEventManager::presetFlagsReceived(LumatoneFirmware::PresetFlags pre LumatoneState::setAftertouchEnabled(presetFlags.polyphonicAftertouch); setStateProperty(LumatoneStateProperty::AftertouchEnabled, presetFlags.polyphonicAftertouch); getEditorListeners()->call(&LumatoneEditor::EditorListener::aftertouchToggled, presetFlags.polyphonicAftertouch); - + LumatoneState::setInvertSustain(presetFlags.sustainPedalInverted); setStateProperty(LumatoneStateProperty::InvertSustain, presetFlags.sustainPedalInverted); getEditorListeners()->call(&LumatoneEditor::EditorListener::invertSustainToggled, presetFlags.sustainPedalInverted);