Hi all,
Usual disclaimer first: I do not work for Bome and this is not official guidance — I am a user sharing what I ran into, and I used an AI assistant to help me read the file format, test the hypotheses and write it up.
After chasing a silence on my live rig for an evening, I found the cause was a naming problem. So I wrote down a convention, together with a script that audits a .bmtp file. Sharing it in case it is useful, and mostly to get it corrected.
The failure it came from. A translator computed a controller number into a variable and put it in the Outgoing Action’s controller slot. A dead-zone branch used ‘Exit rules, execute Outgoing Action’, so it fired before that variable was assigned, emitting CC <whatever was left> = 0. Because the variable was global, ‘whatever was left’ was not bounded by that translator, and some of the values landed in 120-127. Those are Channel Mode messages: All Sound Off, All Notes Off, Poly On. The rig went quiet and nothing in the MIDI monitor looked like a cause.
The convention, in short.
One. Locals are oo pp qq rr ss tt uu vv ww xx and nothing else. Every other name is global whether you meant it or not. Default to a local: they are scoped per incoming event, so two translators using pp can never interfere.
Two. Give each local a fixed job — rr first data byte, pp second, qq channel, ss tt uu scratch, vv ww extra outputs, xx flag — so two values of the same type inside one chain have obvious homes.
Three. One global head letter per device, never per preset index. Presets get renumbered when you drag them, a keyboard does not. The second character carries the type.
Four. Never put a shared global in a controller-number slot, and use ‘Exit rules, don’t execute’ when a branch means ‘send nothing’.
Five. Write the legend in the translator name, in a Comment rule at the top of the chain, and in the preset’s Comments field. A literal backslash-n is a real line break there, and your own example projects use it that way, one variable per line.
The script reads a .bmtp and reports: names outside both documented ranges, globals written by more than one active preset, variables read in a preset that never writes them, variables read but never written anywhere, Outgoing Actions with a variable in the controller-number slot, early exits that fire before the assignment they depend on, and — when the project uses the MIDI Router — translators on a routed input port that can exit with noexecute: on that branch the raw message goes on through the router, as Steve explains below. Read-only.
This overlaps with the ‘Cross-Reference List of all Variables in a MT Pro Project’ thread here and pestrela’s bome_analyse_project.sh, which I only found afterwards. Credit where it is due. Mine is a complement, focused on scope and collisions rather than cross-referencing.
Repository: GitHub - Beennnn/bome-project-tools: Read, audit and safely rewrite Bome MIDI Translator Pro project files (.bmtp) — notes on the format, plus tools that edit it and test the result. Unofficial. · GitHub (MIT, plain Python, no dependency). It has grown into a small toolkit for anyone whose MT Pro project has outgrown checking by eye:
- audit — the read-only report described above, now also covering the MIDI Router;
- probe — send MIDI through the running MT Pro and check what comes out, and that nothing comes out where nothing should — a case that observes nothing only passes if it said it expected silence, so a test aimed at the wrong channel does not slip through;
- project — edit a
.bmtpfrom Python for the bulk changes that are tedious by hand: it takes a backup first, refuses to run while MT Pro is open, and leaves an unchanged preset byte-identical, so a diff shows exactly your change; - the format notes — everything I have worked out about the file so far, in the README.
It replaces the earlier bome-variable-naming repository.
Corrections so far (thanks Steve): oo is a local too — ten locals, not nine. And globals are not limited to the heads g h i j k l m n y z: a head from o to x followed by any character other than itself (oa, o1…) is global as well. The rest of the scope ranges still comes from the manual and old forum threads, so further corrections are welcome — I would rather fix the document than have people rely on it.
Thanks,