>_EVERY KNOB, WIRED TO ANYTHING
A self-contained, real-time MIDI translation engine in pure Rust. It listens on MIDI input ports, matches each incoming message against translator entries, runs a rules script on a small signed-32-bit integer VM, and fires an outgoing action — MIDI out, keystrokes, mouse, AppleScript, timers, or host-defined commands. It imports and exports BOME MIDI Translator Pro .bmtp projects and stores native projects as JSON. The crate carries no UI of its own: it is the engine meant to be hosted by a GUI/CLI application.
Pipeline
An incoming MIDI message flows through four stages on a dedicated worker thread:
MIDI in ──▶ matcher ──▶ rules VM ──▶ outgoing action (midir) (per-entry) (integer) (MIDI / keys / mouse / AppleScript / timer / custom)
The engine owns the open input ports (midir) and emits EngineEvents back to the host through a sink callback. Actions the host wants to handle itself route through Outgoing::Custom { command } plus a host-installed handler — so the engine maps MIDI to host commands without being modified.
Embedding
The crate is a library (ztranslator). A host wires the engine's events to its own UI and IPC:
use ztranslator::{Engine, EngineConfig, EngineEvent};
let mut engine = Engine::new(EngineConfig::default())?;
engine.set_event_sink(|ev: EngineEvent| { /* forward to UI */ });
engine.set_custom_handler(|cmd: String| { /* host-defined actions */ });
let project = ztranslator::bmtp::import("Ableton.bmtp")?;
engine.load_project(project);
engine.start()?;
engine.open_input("nanoKONTROL2 SLIDER/KNOB")?;
Data model
Project → Preset → Translator, where each Translator carries:
Incoming
The trigger — one of 26 types: MIDI / 14-bit, OSC, Art-Net + sACN DMX, serial, gamepad, HID, MIDI clock, MTC, MMC, Ableton Link, audio, CV/gate, HTTP / WebSocket / MQTT / TCP, RTP-MIDI, keystroke, timer, or cron schedule. The matcher captures the value into the rules VM's inputs.
Rules
An ordered list of rules run on the integer VM before the action fires — conditionals, arithmetic, variable assignment, control flow.
Outgoing
The action — one of 39: MIDI / OSC / DMX / sACN out, MIDI clock + MTC generators, MMC, RTP-MIDI, Ableton Link, HTTP / WebSocket / MQTT / TCP, CV/gate, gamepad rumble, keystroke, mouse, AppleScript, speak / play-sound / clipboard, notify, file write, serial, timer, preset control, or a host-defined custom command.
The model is serde-serializable; native projects are stored as JSON via Project::load_json / Project::save_json. Presets group translators and can be switched at runtime.
Rules language
A faithful implementation of BOME's rules language, compiled and run on a small signed-32-bit integer VM:
Expressions
Arithmetic + - * / % and bitwise & | ^ >> << over signed 32-bit values.
Conditionals
IF a <cmp> b THEN <action> with the six comparators == != >= <= > <.
Control flow
Goto / Label, Skip Next [n], exit-and-execute / ignore, and Log.
Variables
10 locals (oo…xx, one event) and two-character globals (ga…z9, project-lifetime, init 0).
Values wrap on overflow; division / modulo by zero yields 0 — matching BOME's documented semantics exactly. These behaviors are pinned by tests/rules_vm.rs.
.bmtp import + export
bmtp::import(path) parses BOME MIDI Translator Pro project files: the INI container, MID3 / KAM3 XML payloads, timer / EnDi payloads, and the Options rules blob. Export re-serializes a Project back to an unsigned .bmtp that round-trips through import. The RSA signature section is not produced, so a signed export for BomeBox / MT Player is out of scope — the native store is JSON.
Import is lossless: encodings whose binary layout is not yet decoded (older MID1 MIDI, KAM1 keystrokes, mouse, serial, launch) are preserved verbatim, so a project round-trips even when individual entries are not yet natively understood.
Outgoing actions & platforms
The Outgoing enum has 41 variants:
MIDI out
Sent through a midir output port. Cross-platform.
Keystroke / mouse
OS synthesis via macOS CGEvent (incl. mouse wheel), through a keycode table (keycodes.rs).
AppleScript / launch
AppleScript (with global injection) and launch-file with args. macOS only — Error::Unsupported elsewhere.
MIDI route / port
MidiRouterAction and MidiPort manage routes and port open/close at runtime.
Project / preset
ProjectAction (load / save / switch) and PresetControl (enable / disable).
Timer / serial
Internal scheduler (engine/timers.rs) drives timer translators; Serial writes to a serial port.
Custom / Stryke
Custom routes to the host via Engine::set_custom_handler; Stryke runs a stryke script through a host-installed runner hook.
Raw / None
Raw preserves an undecoded action for lossless .bmtp round-trip; None is the no-op.
MIDI I/O and the rules / import core build on all platforms. OS-control actions are macOS-only and require the host application to hold Accessibility permission (System Settings → Privacy & Security → Accessibility). actions::accessibility_trusted() reports the grant; executors return Error::NeedsAccessibility when it is missing.
Architecture
The crate is 20 Rust files, 9,925 source lines (8,484 code). See the engineering report for the full subsystem and per-file breakdown.
engine/
Worker-thread engine: input ports, dispatch loop, all non-MIDI trigger sources, MIDI clock + MTC generators, event sink, timer scheduler, outgoing delay (4,180 lines).
rules.rs
Rules compiler + signed-32-bit integer VM (723 lines).
bmtp/
BOME .bmtp import + export: INI, XML payloads, options, unsigned re-serialize, lossless passthrough (1,540 lines).
model.rs
Project / Preset / Translator model, 39 Outgoing + 26 Incoming variants, JSON load/save (1,323 lines).
actions/ + keycodes.rs
macOS CGEvent keystroke / mouse / AppleScript + say / afplay / pbcopy (716 lines).
sources.rs + serial.rs
OSC / Art-Net / sACN / RTP-MIDI codecs + serial framing (396 lines).
Proprietary · part of the MenkeTechnologies paid stack · engineering report · GitHub