// ZTRANSLATOR — ENGINEERING REPORT

Embeddable real-time MIDI translation engine in pure Rust · worker-thread engine · signed-32-bit rules VM · BOME .bmtp import + export · OS-control actions (keystroke / mouse / AppleScript)

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

13,062
Source Lines
10,556
Code Lines
40
Rust Files
161
Test Functions
3
Integration Test Modules
23
Direct Dependencies
39
Outgoing Actions
26
Incoming Trigger Types

Source Distribution — 13,062 lines

12,426 library / 636 tests · 95.1% library

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.

SubsystemKey FilesLines%ShareDescription
Music / MIDI 2.0 Layertheory, spelling, voicing, tuning, mpe, gm, drummap, ccmap, chanmatrix, sysex, smf, stream, midi2, transform, smooth, timing, clock, generators, groove, arp4,15431.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
Engineengine/mod, engine/timers3,62627.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 / Exportbmtp/mod, bmtp/options, bmtp/payload, bmtp/export1,39610.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 Modelmodel1,1849.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 VMrules6444.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
Actionsactions/mod, actions/macos, keycodes7055.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 Sourcessources, serial3863.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/Omidi1271.0%
MIDI message types and midir input/output port plumbing — cross-platform, builds on macOS / Linux / Windows
Matchermatcher1170.9%
Per-translator incoming-trigger matching against live MIDI messages: note / CC / pitch-bend / channel selectivity, value capture into rules-VM inputs
Error / Entryerror, lib870.7%
Crate Error type (Unsupported, NeedsAccessibility, parse / IO variants via thiserror) and the public lib.rs surface re-exports
Teststests/bmtp_import, tests/engine_pipeline, tests/rules_vm6364.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)
TOTAL13,062100%

$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.

FileLinesRole
src/engine/mod.rs3,536Worker-thread engine, dispatch loop, every non-MIDI trigger source, MIDI clock + MTC generators, port management, event sink, outgoing delay
src/model.rs1,184Project / Preset / Translator model, 41 Outgoing + 28 Incoming variants, serde JSON load/save
src/rules.rs644Rules compiler + signed-32-bit integer VM
src/bmtp/payload.rs606MID3/KAM3 XML + timer payload decoding
src/transform.rs357Response curves, quantize, deadzone, slew limiting, scale snap, transpose, 14-bit packing
src/sources.rs332OSC / Art-Net / sACN / RTP-MIDI packet (de)coders
src/midi2.rs326MIDI 2.0 UMP codecs, min-center-max bit scaling, (N)RPN builder / parser
src/sysex.rs309SysEx builders: identity, GM/GS/XG resets, Roland checksum, 8↔7-bit packing
tests/bmtp_import.rs293.bmtp import-fidelity + export round-trip integration tests
src/bmtp/mod.rs279.bmtp INI container parse, bmtp::import entry point
src/keycodes.rs264Keystroke-name → platform keycode table
src/groove.rs264Swing quantize, groove templates, chord strum, note-repeat roll, velocity compressor
src/bmtp/options.rs257Options rules-blob parser
src/tuning.rs25612-TET frequency math, cents ↔ pitch-bend, Scala .scl parsing, MTS tuning SysEx
src/actions/macos.rs255macOS CGEvent keystroke / mouse (incl. wheel) / AppleScript synthesis, Accessibility check
src/bmtp/export.rs254Unsigned .bmtp re-serializer — Project.bmtp, round-trips through import
src/gm.rs248General MIDI program map (128 programs, 16 families) + Bank Select / Program Change builders
src/stream.rs235Raw MIDI byte-stream framing: running-status parse + re-serialize
tests/engine_pipeline.rs233End-to-end engine dispatch integration tests
src/theory.rs230Note-name ↔ MIDI number, chord construction, in-scale note enumeration
src/generators.rs230LFO (6 shapes incl. sample-and-hold) + ADSR envelope value-over-time generators
src/timing.rs195BPM ↔ 24-PPQN clock interval, Song Position Pointer, debounce, auto-repeat
src/arp.rs187Arpeggiator (up/down/updown/downup/converge, octaves) + step-sequencer models
src/actions/mod.rs186Action dispatch + macOS say / afplay / pbcopy; Unsupported/NeedsAccessibility gating
src/clock.rs178Tap tempo, note-value durations, tick ↔ millisecond, bar/beat/tick location
src/voicing.rs177Chord inversion, drop voicings, open/close spacing, voice leading
src/chanmatrix.rs171Channel allow/block masks and a 16×16 channel routing matrix
src/ccmap.rs163CC assignment names, MSB/LSB 14-bit pairing, Channel Mode messages (CC 120–127)
src/drummap.rs149General MIDI percussion map + drum-note remapping
src/smf.rs148Standard MIDI File variable-length quantities, MThd header, MTrk writer
src/mpe.rs127MPE zone layout, MPE Configuration Message, bend-range setup, voice allocator
src/midi.rs127MIDI message types + midir port plumbing
src/matcher.rs117Per-translator incoming-trigger matching
tests/rules_vm.rs110Rules-VM semantics integration tests
src/spelling.rs107Key-signature-aware enharmonic note spelling
src/smooth.rs97Exponential moving average, median filter, hysteresis, interpolation ramp
src/engine/timers.rs90Timer trigger scheduler
src/serial.rs54Serial send + hex/text data framing
src/lib.rs50Public crate surface re-exports
src/error.rs37Crate Error enum (thiserror)
TOTAL13,06240 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 (gaz9, 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.

CrateRoleScope
midirCross-platform MIDI input/output portsall platforms
serdeDerive-based serialization of the project modelall platforms
serde_jsonNative project storage (JSON)all platforms
thiserrorCrate Error enum derivationall platforms
crossbeam-channelEngine ↔ host event / command channelsall platforms
serialportSerial port I/O — incoming + outgoing serial triggers/actionsall platforms
roscOSC message (de)coding for OSC triggers and actionsall platforms
notifyFilesystem-watch triggersall platforms
gilrsGamepad / controller input (axes + buttons → triggers)all platforms
tiny_httpTiny HTTP server for incoming HTTP triggersall platforms
ureqBlocking HTTP client for outgoing GET/POST actionsall platforms
tungsteniteWebSocket client/server (incoming + outgoing)all platforms
rumqttcMQTT client (incoming + outgoing messages)all platforms
hidapiGeneric USB-HID input (Stream Deck / custom controllers)all platforms
cronCron-expression parsing for time-of-day triggersall platforms
chronoLocal-time computation for cron schedulesall platforms
if-addrsEnumerate active network interfaces (system-event triggers)all platforms
socket2Multicast socket options (SO_REUSEADDR/PORT) for sACN inputall platforms
rusty_linkAbleton Link tempo / beat sync (beat triggers, transport out)all platforms
core-graphicsmacOS CGEvent keystroke / mouse synthesismacOS
core-foundationmacOS AppleScript / Accessibility plumbingmacOS
foreign-typesmacOS Core Foundation / Core Graphics FFI wrappersmacOS
cpalmacOS 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.