>_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.
Music & MIDI 2.0 layer
Twenty pure, side-effect-free modules sit on top of the raw MIDI byte. They hold no state and open no ports, so each is unit-tested in isolation and callable from a rule, from the host, or from the editor. Nothing in this layer has a BOME counterpart — BOME treats a note as a bare 0–127 integer.
theory.rs · spelling.rs · voicing.rs
Note name ↔ MIDI number (scientific pitch), chord construction, in-scale enumeration; key-signature-aware enharmonic spelling (Eb, not D#, in flat keys); inversion, drop voicings, open/close spacing, voice leading.
tuning.rs · mpe.rs
12-TET frequency math, cents ↔ pitch-bend, Scala .scl scale files, MIDI Tuning Standard 3-byte frequencies + real-time single-note tuning SysEx; MPE zone layout, the MPE Configuration Message, per-note bend-range setup, least-loaded voice allocation.
gm.rs · drummap.rs · ccmap.rs
The 128 GM Level 1 programs and 16 families plus Bank-Select-then-Program-Change builders; the GM percussion map and drum-note remapping; CC assignment names, MSB/LSB 14-bit pairing, and the seven Channel Mode messages (CC 120–127).
sysex.rs · smf.rs · stream.rs
SysEx identity request, GM / Roland GS / Yamaha XG resets, Universal Real Time master volume, Roland checksums, 8↔7-bit packing; Standard MIDI File variable-length quantities, MThd header, MTrk writer; running-status stream parse + re-serialize with interleaved real-time bytes.
midi2.rs · chanmatrix.rs
MIDI 2.0 Universal MIDI Packet codecs (type 0x2 and 0x4), spec-accurate min-center-max 7↔16-bit scaling, (N)RPN build / parse; 16-bit channel allow / block masks and a 16×16 routing matrix that drops, passes, or fans a channel-voice message out.
transform.rs · smooth.rs
Response curves, quantize, analog deadzone, slew limiting, musical-scale snap, transpose, 14-bit packing; exponential moving average, median filter, hysteresis, and an interpolation ramp for CC fades.
timing.rs · clock.rs
BPM ↔ 24-PPQN clock interval, Song Position Pointer pack / unpack, debounce and auto-repeat; tap tempo, note-value (1/8., 1/8t, …) durations, tick ↔ millisecond at arbitrary PPQ, bar/beat/tick location.
generators.rs · groove.rs · arp.rs
LFO with six shapes including sample-and-hold, and an ADSR envelope, both returning a MIDI value for a given phase / elapsed time; swing quantize, named groove templates, chord strum, note-repeat rolls with a velocity ramp, velocity compression; arpeggiator orders (up / down / updown / downup / converge, octaves) and a step-sequencer step + gate model.
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 40 Rust files — 37 under src/ plus 3 integration modules under tests/ — totalling 13,062 source lines (10,556 code). See the engineering report for the full subsystem and per-file breakdown.
music / MIDI 2.0 layer
Twenty pure modules with no BOME counterpart: theory, spelling, voicing, tuning, mpe, gm, drummap, ccmap, chanmatrix, sysex, smf, stream, midi2, transform, smooth, timing, clock, generators, groove, arp (4,154 lines).
engine/
Worker-thread engine: input ports, dispatch loop, all non-MIDI trigger sources, MIDI clock + MTC generators, event sink, timer scheduler, outgoing delay (3,626 lines).
bmtp/
BOME .bmtp import + export: INI, XML payloads, options, unsigned re-serialize, lossless passthrough (1,396 lines).
model.rs
Project / Preset / Translator model, 41 Outgoing + 28 Incoming variants, JSON load/save (1,184 lines).
rules.rs
Rules compiler + signed-32-bit integer VM (644 lines).
actions/ + keycodes.rs
macOS CGEvent keystroke / mouse / AppleScript + say / afplay / pbcopy (705 lines).
sources.rs + serial.rs
OSC / Art-Net / sACN / RTP-MIDI codecs + serial framing (386 lines).
Proprietary · part of the MenkeTechnologies paid stack · engineering report · GitHub