// ZTRANSLATOR — REAL-TIME MIDI ROUTING ENGINE

Embeddable engine · MIDI / keystroke / mouse / AppleScript routing · signed-32-bit rules VM · BOME .bmtp import · JSON project model · pure Rust

9,925 source lines · 8,484 code · 20 Rust files · 65 tests

Report Port GitHub
// Color scheme

>_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