>_EXECUTIVE SUMMARY
ztranslator is a self-contained, real-time MIDI translation engine written in pure Rust, meant to be embedded inside a host GUI/CLI application. It listens on MIDI input ports (via midir), matches each incoming message against per-translator triggers, runs a rules script on a small signed-32-bit integer VM, and fires an outgoing action — one of 39 Outgoing variants spanning MIDI out (incl. 14-bit / NRPN), keystroke / mouse / AppleScript synthesis (macOS), MIDI route / port / project control, MIDI clock + MTC generators, MMC transport, OSC, Art-Net + sACN DMX, HTTP / WebSocket / MQTT / raw TCP, RTP-MIDI, Ableton Link, CV / gate, gamepad rumble, serial, file write, notify / speak / clipboard, timer, preset control, a host-defined custom command, and a stryke-script hook. Beyond MIDI it accepts 26 incoming trigger types (OSC, DMX, sACN, serial, gamepad, HID, MIDI clock, MTC, MMC, Ableton Link, audio amplitude/onset/pitch, CV/gate, cron schedule, system events, HTTP, TCP, WebSocket, MQTT, RTP-MIDI). It imports BOME MIDI Translator Pro .bmtp projects (INI container + MID3/KAM3 XML payloads + Options rules blob) and exports native projects back to unsigned .bmtp (round-tripping through import), with serde JSON as the native store. The crate carries no UI of its own: the host wires the engine's EngineEvent stream to its interface and routes Outgoing::Custom back through a handler. Layered on top of the raw byte is a pure music / MIDI 2.0 layer that no remapper ships: note-name and chord/scale theory, enharmonic spelling, voicing and inversion, tuning (12-TET, cents, Scala .scl, MTS), MPE zones, GM program and percussion maps, CC naming and 14-bit pairing, a channel routing matrix, SysEx builders, Standard MIDI File primitives, running-status stream framing, MIDI 2.0 UMP codecs, value transforms and smoothing, LFO / ADSR generators, groove and strum, and arpeggiator / step-sequencer models. 13,062 source lines (10,556 code) across 40 Rust files, a worker-thread engine, a faithful BOME rules VM, lossless .bmtp import + export, a broad protocol I/O layer, and a music-theory layer — the routing core behind a MIDI-controlled desktop application.
Source Distribution — 13,062 lines
Library: 37 files under src/ (engine, rules VM, model, bmtp import/export, actions, protocol sources, midi, error, and the music / MIDI 2.0 layer). Tests: 3 integration modules under tests/ (bmtp_import.rs, engine_pipeline.rs, rules_vm.rs). Line counts are physical wc -l; code-only (tokei, blanks + comments excluded) is 10,556.
#SUBSYSTEM BREAKDOWN
Source partitioned by role. The music / MIDI 2.0 layer is now the largest slice at 31.8% — twenty pure, unit-tested modules (theory, spelling, voicing, tuning, MPE, GM program + drum maps, CC map, channel matrix, SysEx, SMF, stream framing, MIDI 2.0 UMP, transform, smooth, timing, clock, generators, groove, arp) that have no BOME counterpart. The engine (worker thread + dispatch + protocol sources + generators + timers) is second at 27.8%; .bmtp import + export is 10.7% because BOME's binary container format needs dedicated INI / XML / options parsers on the way in and an unsigned re-serializer on the way out. The data model is 9.1%, carrying 28 Incoming and 41 Outgoing variants (26 trigger types and 39 actions, plus Raw and None in each). The rules VM is a self-contained 644-line interpreter.
| Subsystem | Key Files | Lines | % | Share | Description |
|---|---|---|---|---|---|
| Music / MIDI 2.0 Layer | theory, spelling, voicing, tuning, mpe, gm, drummap, ccmap, chanmatrix, sysex, smf, stream, midi2, transform, smooth, timing, clock, generators, groove, arp | 4,154 | 31.8% | Twenty pure, side-effect-free modules layered on the raw MIDI byte: note-name / chord / scale theory, key-aware enharmonic spelling, chord voicing + inversion + voice leading, tuning (12-TET frequency, cents ↔ pitch-bend, Scala .scl, MTS single-note tuning SysEx), MPE zones + voice allocation, GM program and percussion maps, CC naming + 14-bit MSB/LSB pairing + Channel Mode builders, a 16×16 channel routing matrix, SysEx builders (identity, GM/GS/XG resets, Roland checksum, 8↔7-bit packing), SMF variable-length quantities + MThd/MTrk, running-status stream framing, MIDI 2.0 UMP codecs + min-center-max bit scaling + (N)RPN, value transforms (curves, quantize, deadzone, slew, scale snap), smoothing (EMA, median, hysteresis, ramp), tempo / SPP / debounce / auto-repeat timing, tap tempo + note-value durations, LFO + ADSR generators, swing / groove templates / strum / note-repeat / velocity compression, and arpeggiator + step-sequencer models | |
| Engine | engine/mod, engine/timers | 3,626 | 27.8% | Worker-thread engine: opens MIDI input ports (midir), runs the dispatch loop, hosts every non-MIDI trigger source (OSC / Art-Net / sACN / serial / gamepad / HID / HTTP / WebSocket / MQTT / TCP / audio / CV / Ableton Link / RTP-MIDI) and the MIDI clock + MTC generators, emits EngineEvents, routes Outgoing::Custom to the host, applies outgoing delay, and schedules timers | |
| .bmtp Import / Export | bmtp/mod, bmtp/options, bmtp/payload, bmtp/export | 1,396 | 10.7% | BOME MIDI Translator Pro project I/O: INI container, MID3/KAM3 XML payloads, timer/EnDi payloads, and the Options rules blob on import; unsigned .bmtp re-serialization on export (round-trips through import). RSA signature ignored; undecoded encodings preserved verbatim for lossless round-trip | |
| Data Model | model | 1,184 | 9.1% | Project → Preset → Translator, each translator carrying one of 28 Incoming variants (26 trigger types plus Raw and None) and one of 41 Outgoing variants (39 actions plus Raw and None). serde-serializable (snake_case tagged enums); native projects via Project::load_json / save_json | |
| Rules VM | rules | 644 | 4.9% | Faithful BOME rules implementation on a signed-32-bit integer VM: assignment, arithmetic (+ - * / %) + bitwise (& | ^ >> <<), IF/THEN with 6 comparators, Goto/Label, Skip Next [n], Log, exit-and-execute/ignore. 10 locals + two-character globals; wrap-on-overflow; div/mod by zero = 0 | |
| Actions | actions/mod, actions/macos, keycodes | 705 | 5.4% | Outgoing OS-control actions: keystroke / mouse (incl. wheel) synthesis and AppleScript / launch via macOS CGEvent, plus say / afplay / pbcopy (speak / play-sound / clipboard); keystroke-name → platform keycode table. macOS-only — Error::Unsupported elsewhere | |
| Protocol Sources | sources, serial | 386 | 3.0% | Pure (de)coders for the non-MIDI protocols: OSC (rosc), Art-Net + sACN/E1.31 DMX, AppleMIDI/RTP-MIDI exchange + clock-sync + data packets, and serial hex/text framing — all unit-tested independent of the live sockets | |
| MIDI I/O | midi | 127 | 1.0% | MIDI message types and midir input/output port plumbing — cross-platform, builds on macOS / Linux / Windows | |
| Matcher | matcher | 117 | 0.9% | Per-translator incoming-trigger matching against live MIDI messages: note / CC / pitch-bend / channel selectivity, value capture into rules-VM inputs | |
| Error / Entry | error, lib | 87 | 0.7% | Crate Error type (Unsupported, NeedsAccessibility, parse / IO variants via thiserror) and the public lib.rs surface re-exports | |
| Tests | tests/bmtp_import, tests/engine_pipeline, tests/rules_vm | 636 | 4.9% | Integration modules (plus a tests/fixtures corpus) covering .bmtp import fidelity + export round-trip, end-to-end engine dispatch, and rules-VM semantics; the unit tests in-crate cover the protocol codecs, MTC/MMC, the whole music / MIDI 2.0 layer, and trigger/action serde round-trips (161 #[test] functions in all — 128 inline, 33 integration) | |
| TOTAL | 13,062 | 100% | |||
$FILES BY SIZE
All 40 Rust files (37 under src/, 3 under tests/), largest first. The engine dominates a single file; the four bmtp/ files together (1,396 lines) are the cost of natively reading and re-writing BOME's binary project container; the twenty music / MIDI 2.0 modules (4,154 lines) are pure and independently testable.
| File | Lines | Role |
|---|---|---|
| src/engine/mod.rs | 3,536 | Worker-thread engine, dispatch loop, every non-MIDI trigger source, MIDI clock + MTC generators, port management, event sink, outgoing delay |
| src/model.rs | 1,184 | Project / Preset / Translator model, 41 Outgoing + 28 Incoming variants, serde JSON load/save |
| src/rules.rs | 644 | Rules compiler + signed-32-bit integer VM |
| src/bmtp/payload.rs | 606 | MID3/KAM3 XML + timer payload decoding |
| src/transform.rs | 357 | Response curves, quantize, deadzone, slew limiting, scale snap, transpose, 14-bit packing |
| src/sources.rs | 332 | OSC / Art-Net / sACN / RTP-MIDI packet (de)coders |
| src/midi2.rs | 326 | MIDI 2.0 UMP codecs, min-center-max bit scaling, (N)RPN builder / parser |
| src/sysex.rs | 309 | SysEx builders: identity, GM/GS/XG resets, Roland checksum, 8↔7-bit packing |
| tests/bmtp_import.rs | 293 | .bmtp import-fidelity + export round-trip integration tests |
| src/bmtp/mod.rs | 279 | .bmtp INI container parse, bmtp::import entry point |
| src/keycodes.rs | 264 | Keystroke-name → platform keycode table |
| src/groove.rs | 264 | Swing quantize, groove templates, chord strum, note-repeat roll, velocity compressor |
| src/bmtp/options.rs | 257 | Options rules-blob parser |
| src/tuning.rs | 256 | 12-TET frequency math, cents ↔ pitch-bend, Scala .scl parsing, MTS tuning SysEx |
| src/actions/macos.rs | 255 | macOS CGEvent keystroke / mouse (incl. wheel) / AppleScript synthesis, Accessibility check |
| src/bmtp/export.rs | 254 | Unsigned .bmtp re-serializer — Project → .bmtp, round-trips through import |
| src/gm.rs | 248 | General MIDI program map (128 programs, 16 families) + Bank Select / Program Change builders |
| src/stream.rs | 235 | Raw MIDI byte-stream framing: running-status parse + re-serialize |
| tests/engine_pipeline.rs | 233 | End-to-end engine dispatch integration tests |
| src/theory.rs | 230 | Note-name ↔ MIDI number, chord construction, in-scale note enumeration |
| src/generators.rs | 230 | LFO (6 shapes incl. sample-and-hold) + ADSR envelope value-over-time generators |
| src/timing.rs | 195 | BPM ↔ 24-PPQN clock interval, Song Position Pointer, debounce, auto-repeat |
| src/arp.rs | 187 | Arpeggiator (up/down/updown/downup/converge, octaves) + step-sequencer models |
| src/actions/mod.rs | 186 | Action dispatch + macOS say / afplay / pbcopy; Unsupported/NeedsAccessibility gating |
| src/clock.rs | 178 | Tap tempo, note-value durations, tick ↔ millisecond, bar/beat/tick location |
| src/voicing.rs | 177 | Chord inversion, drop voicings, open/close spacing, voice leading |
| src/chanmatrix.rs | 171 | Channel allow/block masks and a 16×16 channel routing matrix |
| src/ccmap.rs | 163 | CC assignment names, MSB/LSB 14-bit pairing, Channel Mode messages (CC 120–127) |
| src/drummap.rs | 149 | General MIDI percussion map + drum-note remapping |
| src/smf.rs | 148 | Standard MIDI File variable-length quantities, MThd header, MTrk writer |
| src/mpe.rs | 127 | MPE zone layout, MPE Configuration Message, bend-range setup, voice allocator |
| src/midi.rs | 127 | MIDI message types + midir port plumbing |
| src/matcher.rs | 117 | Per-translator incoming-trigger matching |
| tests/rules_vm.rs | 110 | Rules-VM semantics integration tests |
| src/spelling.rs | 107 | Key-signature-aware enharmonic note spelling |
| src/smooth.rs | 97 | Exponential moving average, median filter, hysteresis, interpolation ramp |
| src/engine/timers.rs | 90 | Timer trigger scheduler |
| src/serial.rs | 54 | Serial send + hex/text data framing |
| src/lib.rs | 50 | Public crate surface re-exports |
| src/error.rs | 37 | Crate Error enum (thiserror) |
| TOTAL | 13,062 | 40 files |
@EXECUTION PIPELINE
An incoming MIDI message flows through four stages on the engine's worker thread. The matcher selects translators whose Incoming trigger fits the message; each match runs its rules on the integer VM; the resulting Outgoing action is dispatched to MIDI out, the OS action layer, the timer scheduler, or the host's custom handler.
MIDI in (midir input port)
│
▼
┌─────────────┐ ┌─────────────┐ ┌──────────────┐
│ midi.rs │────▶│ matcher.rs │────▶│ rules.rs │
│ (127) │ │ (117) │ │ (644) │
│ message │ │ per-entry │ │ integer VM │
│ types │ │ trigger │ │ 10 locals │
│ midir port │ │ match + │ │ globals │
│ │ │ capture │ │ IF/Goto │
└─────────────┘ └─────────────┘ └──────┬───────┘
│
┌────────────────────┤
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ engine/mod │ │ actions/ │
│ (3,536) │ │ (441) │
│ dispatch │────▶│ CGEvent │
│ event sink │ │ keystroke │
│ custom │ │ mouse/wheel │
│ handler │ │ AppleScript │
└──────┬───────┘ └──────────────┘
│
┌─────────────┼─────────────┐
│ │ │
┌──────▼──────┐ ┌────▼──────┐ ┌────▼────────┐
│ midir out │ │ timers.rs │ │ host custom │
│ + route/ │ │ (90) │ │ + stryke │
│ port/proj │ │ scheduler │ │ hook │
└─────────────┘ └───────────┘ └─────────────┘
&RULES VM — BOME SEMANTICS
The rules language is a faithful reimplementation of BOME MIDI Translator Pro's rules, run on a small signed-32-bit integer VM. The behaviors below are pinned by tests/rules_vm.rs so refactors can't silently diverge from BOME.
Values & Variables
All values are signed 32-bit and wrap on overflow. Division and modulo by zero yield 0. Variables: 10 locals (oo pp qq rr ss tt uu vv ww xx, lifetime = one incoming event) and two-character globals (ga…z9, initialized to 0, lifetime = project).
Operators
Arithmetic + - * / % and bitwise & | ^ >> <<. Conditionals are IF a <cmp> b THEN <action> with the six comparators == != >= <= > <.
Control Flow
Goto / Label, Skip Next [n], and "exit rules and execute / ignore". Log emits to the host event sink. Outgoing values are mutated in place before the action fires.
Incoming Triggers
A translator's Incoming trigger is one of 26 variants — MIDI message / 14-bit, keystroke, timer, preset, enable/disable, OSC, Art-Net + sACN DMX, file watch, serial, gamepad, HID, MIDI clock, MTC, MMC, Ableton Link, audio, CV/gate, cron schedule, system event, HTTP, TCP, WebSocket, MQTT, or RTP-MIDI. The matcher captures message bytes into VM inputs so rules can branch on note / CC / channel / value.
+DEPENDENCIES
23 direct dependencies — 19 cross-platform, 4 macOS-only. No async runtime: the engine runs on a single dedicated worker thread and talks to the host over crossbeam-channel.
| Crate | Role | Scope |
|---|---|---|
| midir | Cross-platform MIDI input/output ports | all platforms |
| serde | Derive-based serialization of the project model | all platforms |
| serde_json | Native project storage (JSON) | all platforms |
| thiserror | Crate Error enum derivation | all platforms |
| crossbeam-channel | Engine ↔ host event / command channels | all platforms |
| serialport | Serial port I/O — incoming + outgoing serial triggers/actions | all platforms |
| rosc | OSC message (de)coding for OSC triggers and actions | all platforms |
| notify | Filesystem-watch triggers | all platforms |
| gilrs | Gamepad / controller input (axes + buttons → triggers) | all platforms |
| tiny_http | Tiny HTTP server for incoming HTTP triggers | all platforms |
| ureq | Blocking HTTP client for outgoing GET/POST actions | all platforms |
| tungstenite | WebSocket client/server (incoming + outgoing) | all platforms |
| rumqttc | MQTT client (incoming + outgoing messages) | all platforms |
| hidapi | Generic USB-HID input (Stream Deck / custom controllers) | all platforms |
| cron | Cron-expression parsing for time-of-day triggers | all platforms |
| chrono | Local-time computation for cron schedules | all platforms |
| if-addrs | Enumerate active network interfaces (system-event triggers) | all platforms |
| socket2 | Multicast socket options (SO_REUSEADDR/PORT) for sACN input | all platforms |
| rusty_link | Ableton Link tempo / beat sync (beat triggers, transport out) | all platforms |
| core-graphics | macOS CGEvent keystroke / mouse synthesis | macOS |
| core-foundation | macOS AppleScript / Accessibility plumbing | macOS |
| foreign-types | macOS Core Foundation / Core Graphics FFI wrappers | macOS |
| cpal | macOS CoreAudio audio-reactive input (peak level → trigger) | macOS |
Line counts measured with tokei 14.0.0 + wc -l across src/ + tests/; test-function count is #[test] attributes in the same trees. Numbers refresh as the crate adds commits.