From ed47130654385a265269f1f01c64b6b1be83f750 Mon Sep 17 00:00:00 2001 From: John Remillard Date: Wed, 22 Jul 2026 23:47:24 -0400 Subject: [PATCH 1/4] feat: implement MIDI unlearning logic for hardware controls (#217) --- modalapi/modhandler.py | 9 ++- pistomp/handler.py | 36 +++++++-- .../bound.png | Bin 0 -> 7864 bytes .../unbound.png | Bin 0 -> 7490 bytes .../bound.png | Bin 0 -> 8438 bytes .../unbound.png | Bin 0 -> 8438 bytes tests/v3/test_midi_learn.py | 76 ++++++++++++++++++ 7 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_encoder_clears_binding_and_updates_lcd/bound.png create mode 100644 tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_encoder_clears_binding_and_updates_lcd/unbound.png create mode 100644 tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_footswitch_clears_binding/bound.png create mode 100644 tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_footswitch_clears_binding/unbound.png diff --git a/modalapi/modhandler.py b/modalapi/modhandler.py index a9cfbfc1f..745f066ab 100755 --- a/modalapi/modhandler.py +++ b/modalapi/modhandler.py @@ -1208,23 +1208,26 @@ def bind_current_pedalboard(self): # any real time settings self._controller_manager.bind(self.current) - def _redraw_after_binding(self, controller: Controller, is_footswitch: bool) -> None: - if is_footswitch: + def _redraw_after_binding(self, controller: Controller | None, is_footswitch: bool) -> None: + if is_footswitch and controller is not None: # Footswitch: redraw just that one switch, not the whole board. self.lcd.update_footswitch(controller) else: self.lcd.draw_analog_assignments(self.current.analog_controllers) def _add_learned_binding_row( - self, plugin: Plugin, param: Parameter, controller: Controller, old_binding: str | None + self, plugin: Plugin, param: Parameter, controller: Controller | None, old_binding: str | None ) -> None: layer = self._controller_manager.effective_table.layers[0] if old_binding is not None: _remove_binding_row(layer, old_binding) + if controller is None: + return if isinstance(controller, Footswitch): cls, event_kind = ControlClass.FOOTSWITCH, EventKind.PRESS else: cls, event_kind = ControlClass.ANALOG, EventKind.ROTATE + assert param.binding is not None layer.add( BindingDecl( control=ControlRef(cls=cls, id=param.binding), diff --git a/pistomp/handler.py b/pistomp/handler.py index 3c1b5fd6b..13de193e7 100755 --- a/pistomp/handler.py +++ b/pistomp/handler.py @@ -209,9 +209,10 @@ def hide_fullscreen_panel(self) -> None: def _apply_midi_binding( self, instance: str, symbol: Symbol, binding: str, binding_range: tuple[float, float] | None = None ) -> None: - # A MIDI mapping was learned in mod-ui. Update the matching parameter's - # binding and wire its hardware controller so the LCD reflects it without - # a pedalboard reload. Idempotent: replayed connect-dump maps are no-ops. + # A MIDI mapping was learned or cleared in mod-ui. Update the matching + # parameter's binding and wire/unwire its hardware controller so the LCD + # reflects it without a pedalboard reload. Idempotent: replayed connect-dump + # maps are no-ops. if self._current is None: return plugin = self.current.pedalboard.find_plugin(instance) @@ -226,9 +227,31 @@ def _apply_midi_binding( param.set_binding_range(binding_range) if param.binding == binding: return + + old_binding = param.binding controller = self.hardware.controllers.get(binding) + old_controller = self.hardware.controllers.get(old_binding) if old_binding is not None else None + + if old_controller is not None and old_binding != binding: + old_controller.unbind_from_parameter() + if old_controller in plugin.controllers: + plugin.controllers.remove(old_controller) + if isinstance(old_controller, Footswitch): + plugin.has_footswitch = any( + isinstance(c, Footswitch) for c in plugin.controllers + ) + elif isinstance(old_controller, (AnalogMidiControl, EncoderController)): + key = "%s:%s" % (plugin.instance_id, param.name) + self.current.analog_controllers.pop(key, None) + if controller is None: + param.binding = None + self._add_learned_binding_row(plugin, param, None, old_binding) + if old_controller is not None: + is_footswitch = isinstance(old_controller, Footswitch) + self._redraw_after_binding(old_controller, is_footswitch) return + # Externally-routed controls aren't bound to plugin parameters; board # load ignores such bindings (_bind_plugin_parameters) and the live # learn must agree, or the control's MidiCcEffect row shadows the @@ -238,8 +261,7 @@ def _apply_midi_binding( f"MIDI learn for {instance}:{param.name} names external controller " f"{binding} (routed to {self.hardware.external_port_name(controller)}) - ignoring" ) - return - old_binding = param.binding + param.binding = binding is_footswitch = self._bind_controller_to_param(plugin, param, controller) self._add_learned_binding_row(plugin, param, controller, old_binding) @@ -263,13 +285,13 @@ def _bind_controller_to_param(self, plugin: "Plugin", param: "Parameter", contro self.current.analog_controllers[key] = display_info return False - def _redraw_after_binding(self, controller, is_footswitch): + def _redraw_after_binding(self, controller: Controller | None, is_footswitch: bool) -> None: # Refresh the LCD after a learned binding. Subclasses redraw at their # own granularity. raise NotImplementedError() def _add_learned_binding_row( - self, plugin: "Plugin", param: "Parameter", controller: Controller, old_binding: str | None + self, plugin: "Plugin", param: "Parameter", controller: Controller | None, old_binding: str | None ) -> None: # Add a table row for a live-learned binding so dispatch and badges # reflect it without a pedalboard reload. MOD subclasses override; diff --git a/tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_encoder_clears_binding_and_updates_lcd/bound.png b/tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_encoder_clears_binding_and_updates_lcd/bound.png new file mode 100644 index 0000000000000000000000000000000000000000..62631f9f57459f7fb27792614ac77b022252054c GIT binary patch literal 7864 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 Y9+=xO?r;Zx?caY@m9!MA+Z&WdHyG literal 0 HcmV?d00001 diff --git a/tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_footswitch_clears_binding/bound.png b/tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_footswitch_clears_binding/bound.png new file mode 100644 index 0000000000000000000000000000000000000000..295d95bdf005c22bd082cc2ce8c270b37ecfa3b8 GIT binary patch literal 8438 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 Date: Thu, 23 Jul 2026 00:13:05 -0400 Subject: [PATCH 2/4] refactor: clear footswitch bindings explicitly as well --- pistomp/footswitch.py | 6 ++++++ pistomp/lcd320x240.py | 4 ++++ .../unbound.png | Bin 8438 -> 7696 bytes tests/v3/test_midi_learn.py | 2 ++ 4 files changed, 12 insertions(+) diff --git a/pistomp/footswitch.py b/pistomp/footswitch.py index 10f32c31b..2be7023ab 100755 --- a/pistomp/footswitch.py +++ b/pistomp/footswitch.py @@ -99,6 +99,12 @@ def dispatch_key(self) -> str: return f"{self.midi_channel}:{self.midi_CC}" return f"fs:{self.id}" + @override + def unbind_from_parameter(self) -> None: + super().unbind_from_parameter() + self.display_label = None + self.set_category(None) + @property def drives_display(self) -> bool: """True when unbound: no inbound echo will arrive, so the press updates diff --git a/pistomp/lcd320x240.py b/pistomp/lcd320x240.py index 03e5f6929..8f8e2dad6 100644 --- a/pistomp/lcd320x240.py +++ b/pistomp/lcd320x240.py @@ -911,6 +911,10 @@ def update_footswitch(self, footswitch): # Binding may be new (e.g. MIDI learn) — reflect label + color. footswitch.set_display_label(self.footswitch_label(footswitch, slot_w)) wfs.color = accent_color_for(footswitch.category) + wfs.action = self.footswitch_event + else: + wfs.color = None + wfs.action = None wfs.toggle(not footswitch.toggled) wfs.label = footswitch.get_display_label() or "" wfs.refresh() diff --git a/tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_footswitch_clears_binding/unbound.png b/tests/snapshots/v3/test_midi_learn/test_v3_midi_unlearn_footswitch_clears_binding/unbound.png index 295d95bdf005c22bd082cc2ce8c270b37ecfa3b8..0132255fafea3c8347c836e316d5e36a972826c6 100644 GIT binary patch literal 7696 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 literal 8438 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 Date: Thu, 23 Jul 2026 18:08:57 -0400 Subject: [PATCH 3/4] fixed linting error --- plugins/transport/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/transport/__init__.py b/plugins/transport/__init__.py index ef376e81b..46f210ea0 100644 --- a/plugins/transport/__init__.py +++ b/plugins/transport/__init__.py @@ -9,7 +9,7 @@ from __future__ import annotations -from common.parameter import Parameter, Symbol +from common.parameter import Parameter from modalapi.plugin_customization import PluginCustomization from modalapi.pedalboard import BPM_SYMBOL, BPB_SYMBOL, ROLLING_SYMBOL from plugins.customization import register From 19ea5aa9968be9d8801ab68605b2ea4ab8be88b0 Mon Sep 17 00:00:00 2001 From: John Remillard Date: Fri, 24 Jul 2026 01:13:12 -0400 Subject: [PATCH 4/4] test: add binding range update test for same parameter and fix external controller return guard --- pistomp/handler.py | 1 + tests/v3/test_midi_learn.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/pistomp/handler.py b/pistomp/handler.py index 13de193e7..1c22c4e91 100755 --- a/pistomp/handler.py +++ b/pistomp/handler.py @@ -261,6 +261,7 @@ def _apply_midi_binding( f"MIDI learn for {instance}:{param.name} names external controller " f"{binding} (routed to {self.hardware.external_port_name(controller)}) - ignoring" ) + return param.binding = binding is_footswitch = self._bind_controller_to_param(plugin, param, controller) diff --git a/tests/v3/test_midi_learn.py b/tests/v3/test_midi_learn.py index f720ecd6f..e55e1554b 100644 --- a/tests/v3/test_midi_learn.py +++ b/tests/v3/test_midi_learn.py @@ -385,3 +385,39 @@ def test_v3_midi_unlearn_footswitch_clears_binding(v3_system: SystemFixture, mak assert plugin.has_footswitch is False snapshot("unbound") + +def test_v3_midi_learn_updated_binding_range_on_same_parameter( + v3_system: SystemFixture, make_plugin, make_parameter +): + """Re-addressing an already bound parameter to a different sub-range on the same CC + updates the parameter's binding range and endpoints without bailing early.""" + handler = v3_system.handler + hw = v3_system.hw + ws_bridge = v3_system.ws_bridge + + assert handler.current + + enc1 = next(e for e in hw.encoders if getattr(e, "id", None) == 1) + channel, cc = _binding_for(hw, enc1).split(":") + + gain = make_parameter("Gain", "noise", value=0.5) + plugin = make_plugin("noise", bypassed=False, has_footswitch=False, parameters={"gain": gain}) + handler.current.pedalboard.plugins = [plugin] + + # Initial mapping: sub-range 0.0 .. 0.5 + ws_bridge.inject(f"midi_map /graph/noise gain {channel} {cc} 0.0 0.5") + handler.poll_ws_messages() + + assert gain.binding == f"{channel}:{cc}" + assert (gain.minimum, gain.maximum) == (0.0, 0.5) + assert enc1.parameter is gain + + # Updated mapping on SAME binding: sub-range 0.2 .. 0.8 + ws_bridge.inject(f"midi_map /graph/noise gain {channel} {cc} 0.2 0.8") + handler.poll_ws_messages() + + assert gain.binding == f"{channel}:{cc}" + assert (gain.minimum, gain.maximum) == (0.2, 0.8) + assert enc1.parameter is gain + +