Open StompOST-003
2026 –
Reverse-engineered pedal library & editor
Put it backAudio & music crate
Back to Audio & music crateOST-003 · 2026 – · Audio & music

Open Stomp

Reverse-engineered pedal library & editor

I reverse engineered the USB-MIDI and Bluetooth LE protocol of the SORAN Dual Stomp Lite pedal, then built a Rust library, Python bindings and a desktop editor, adding the Linux support the vendor never shipped.

Stack
Rust, Python, Tauri, React
Links
open-stomp-lib ↗ · open-stomp-manager ↗

Open Stomp Manager

Overview

The SORAN Dual Stomp Lite is an amp-modelling guitar pedal. The vendor's editor, Stomp Manager, has no Linux version, and the pedal's protocol isn't documented. Open Stomp is my open-source replacement, in two repos:

  • open-stomp-lib is a Rust crate with Python bindings. It speaks the pedal's protocol over USB-MIDI and Bluetooth LE MIDI, tracks its full state, and converts NAM amp models and cab IRs for its user slots. It also holds the protocol documentation.
  • Open Stomp Manager is a desktop editor for Linux, macOS and Windows, built on the library with Tauri and a React and TypeScript interface.

Everything is tested on a Dual Stomp Lite running firmware V1.1.1. I built it with Claude Code.

Recovering the protocol

Static analysis. Stomp Manager is a Flutter app compiled ahead of time from obfuscated Dart. I dumped its object pool with a patched build of blutter and annotated the disassembly, which turned up the SysEx frame layout, a command table and the BLE-MIDI service. It also showed that a library called imagelib is a disguised copy of NeuralAmpModelerCore.

Live captures. On Apple Silicon you can't capture USB at the bus level without disabling System Integrity Protection. Every byte the editor exchanges with the pedal goes through CoreMIDI, though, so I hooked CoreMIDI inside the app. A small dylib interposes MIDISend, MIDIReceived and related calls and logs every packet. A script copies the app and re-signs the copy without the hardened runtime so DYLD_INSERT_LIBRARIES works, leaving the original untouched. Then I did one action at a time and marked each in the log.

Where the two disagreed, the captures won. The binary suggested a packet-fragmentation prefix that never appears on the wire, for example.

The protocol

Live parameter changes use plain MIDI CCs. Everything else (presets, names, settings, looper, drum machine, tuner, uploads) uses vendor SysEx in one frame shape:

F0  00 21 7A  70  <cmd>  <dir>  <payload ...>  F7
    \_mfr__/  uid  msgId  direction

dir is 0 for a read, 1 for a write, 2 for the pedal's reply and 3 for a write acknowledgement. Big records are 8-bit structs packed into a 7-bit, LSB-first bitstream, so a preset is 126 MIDI bytes that unpack to 110. I worked out the preset layout by correlating all 99 preset records with CC dumps of the same presets.

Over Bluetooth, messages have to go out at least 15 ms apart, because an unpaced burst froze the pedal, and replies take about 95 ms, so a startup sync takes about 19 seconds instead of under one over USB. All of it is written up in nine spec docs.

The library

The protocol layer parses and builds every message the official app sends, with no I/O and no dependencies, so it works with any MIDI transport. On top of it:

  • DeviceState follows all 99 presets, the live sound, looper, drum machine, tuner and settings, and describes each message in plain English, like AMP · Mar J800 · GAIN = 64.
  • A connection engine finds the pedal, follows hotplug, runs the startup sync and paces requests, keeping one SysEx in flight with a 1-second timeout.
  • Fully typed Python bindings wrap the same Rust code with PyO3.
  • Recorded sessions replay through the real engine, so you can develop without a pedal, and they double as test vectors.

Converting NAM models and cab IRs

The pedal can load a NAM amp model into a user slot, but it can't run a neural network. By uploading files with test patterns in them and re-amping through the pedal, I found what it actually runs: two 512-tap FIR filters with a fixed saturation curve between them (a Wiener–Hammerstein model). For cab IRs it plays only the first 384 samples, 8 ms at 48 kHz. Every import is an approximation, and the converter decides how good it is.

The official app measures the model twice and turns each measurement into a minimum-phase filter, without checking whether the result sounds like the model on the pedal. My conversion starts from the same measurements, then fits the filters:

  1. Simulate the pedal: both filters around a saturation curve fitted from re-amp recordings. The simulator predicts the hardware within about 0.5 dB.
  2. Compare the simulated pedal with the model on sweeps, tones and pink noise at several levels, using a multi-resolution spectral loss.
  3. Adjust the filters with hand-written gradients and Adam for about a minute, keeping them smooth and minimum-phase.

For IRs, it designs a 384-tap minimum-phase filter that matches the whole IR, not just its first 8 ms, because many cab IRs start late or ring longer than that.

Results

Measured on hardware, comparing the pedal's USB audio output with the original running in software:

  • A 3-minute bass DI through an Ampeg SVT model and cab: overall tone error (40 Hz to 10 kHz) of 2.1 dB RMS, against 9.4 dB for the official app. Mine was closest to the original in 87% of 100 ms windows, the official app in 8%.
  • The low end (40 to 160 Hz) is the one place it doesn't win, at 3.3 dB against 3.0. A deep low resonance needs longer filters than the pedal has.
  • Cab IRs: 2.1 to 5.2 dB RMS from the full IR, against 5.3 to 16.1 dB for the official upload. One IR starts with 21 ms of silence, so the official upload plays almost none of the cab.

The editor and status

Open Stomp Manager connects by itself and syncs both ways: changes made on the pedal show up immediately. A hub thread owns the engine and the pedal state, and at most every 16 ms it sends the UI only the data sections that changed, so a knob sweep doesn't resend all 99 presets.

Live control, state tracking and imports are verified on hardware over USB and Bluetooth. Preset saving and settings writes send byte-identical messages to the official app but haven't been run on hardware yet, and LSTM and ConvNet NAM models aren't supported yet.