Skip to content

Blend mode: blend between snapshots#150

Merged
sastraxi merged 12 commits into
TreeFallSound:pistomp-v3from
sastraxi:feat/blend-mode
Jun 5, 2026
Merged

Blend mode: blend between snapshots#150
sastraxi merged 12 commits into
TreeFallSound:pistomp-v3from
sastraxi:feat/blend-mode

Conversation

@sastraxi

Copy link
Copy Markdown
Collaborator

This PR introduces "blend mode", which allows a single analog input (e.g. the expression pedal) to interpolate parameter values between multiple snapshots. This is essentially a workaround for the fact that MOD pedalboard does not let you map multiple MIDI CCs to control the same parameter, and also that it does not seem to let you change the extents easily (at least in the UI).

The workflow is such: create your pedalboard, attach footswitches for MIDI controls as usual, etc. Then, create one or more snapshots that make the board sound to your liking. We can then create a new "Blend Mode" preset by configuring the pedalboard.

Configuration

In $HOME/.pedalboards/$PEDALBOARD/config.yml:

blend_snapshots:
  - name: "Clean to Fuzz"          # Required: descriptive name for this blend snapshot
    input_id: 0                    # Required: expression pedal (0) or encoder (1, 2)
    interpolation: linear          # Optional: interpolation function (default: linear)
                                   # Splines: hermite, catmull_rom
                                   # Easing: linear, ease_in_quad, ease_out_quad, ease_in_out_quad,
                                   #         ease_in_cubic, ease_out_cubic, ease_in_out_cubic,
                                   #         exponential, sine
    stops:                         # Position (stringified float) -> snapshot (index or name)
      "0.0": 0                     # Snapshot by index (0-based)
      "0.5": "Crunch"              # Or by name (case-insensitive prefix match)
      "1.0": "Heavy"               # Min separation: 1/127 (0.0079)
  - name: "Volume Swell"
    input_id: 1                    # Tweak1 encoder
    stops: ["Quiet", "Loud"]       # List format: auto-spaced evenly

Enabling and hooks

When we load a pedalboard with blend snapshots configured, we create a new snapshot for each . Whenever this snapshot is selected, we set it as the active_blend_mode in the handler. If we change / remove any of these blend snapshots, we automatically modify / delete them when we next switch to that pedalboard.

When the pedalboard is saved (and snapshots.json has been written, which requires saving the pedalboard, not just the snapshot), the generated snapshots are re-generated to sync constant values.

How blend mode works

We take over the chosen analog input by adding a callback to it that will override its default behaviour (e.g. sending a MIDI CC). We convert the value to a float (0..1) and use it to discover the stops that this value is contained within. Then, we discover which parameters differ between the two snapshots these stops are configured to use, and interpolate the parameters between them using websocket messages, e.g.

param_set /graph/StompBox_Fuzz/gain 0.654

Review checklist

  • What do you think of creating these "special" snapshots?
  • The analog callbacks are a bit hacky. I would love feedback on a better way to do this.

@rreichenbach rreichenbach left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even without blend config defined, this is causing a regression for me. Changing the Snapshot via the Nav encoder, doesn't re-render the status for each plugin accordingly on the LCD.

It's possible that the code for this PR is in a weird state, but I have to say that I have reservations about this one.

It's cool, but I'm pretty sure less than 1% of users will ever use it. It's very advanced to set up and to understand.

If it didn't modify the regular/non-blend code path at all, I might be more keen on it, but it does. It adds code complexity, 7 new python files, and potential maintenance overhead.

For those who do use it, I'm imagining support calls (emails). There's a lot to get wrong in the config file.

So unless, you really think you need this. I'm suggesting against merging this.

UPDATE:
Actually... I was misunderstanding how this is configured (was thinking individual settings would need to get mapped, etc.) and didn't fully consider the power of this in a performance setting.

Forgive my skepticism above. If we can get this to work, I'm ok with the complexity hit. We need to figure out why it's messing with regular (non-blend) snapshot changes. My guess is that preset_change_plugin_update() isn't working like it used to.

@sastraxi sastraxi force-pushed the feat/blend-mode branch 2 times, most recently from 28d6aea to b38083e Compare June 4, 2026 01:55
sastraxi and others added 4 commits June 3, 2026 21:59
# Conflicts:
#	modalapi/modhandler.py
#	tests/conftest.py
#	tests/integration/conftest.py
#	tests/test_websocket_bridge.py
…r/send_bpm return bool

feat/blend-mode declares WebSocketBridgeProtocol.send_parameter() -> bool (blend/types.py) and uses the return value for dedup-tracking. feat/websocket-parameter-set implements AsyncWebSocketBridge with -> None. Neither leaf can own the signature alignment.

Why: blend dedup needs to know when a parameter write was suppressed by backpressure; the protocol signature must match the implementation.
How to apply: update AsyncWebSocketBridge.send_parameter / send_bpm to return bool with backpressure check; mirror in FakeWebSocketBridge; wire reset_tracking() into the blend fixture so dedup state is clean across tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@rreichenbach rreichenbach left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. thanks.

@sastraxi sastraxi merged commit 5a9c241 into TreeFallSound:pistomp-v3 Jun 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants