A Behringer X32 keeps its entire state as plain text — one parameter per line, a little over 2,100 lines for a full console. Input patching, head amps, every EQ band, every monitor mix, the routing out to the stage boxes. You can open it in a text editor, which makes it look like a file you can edit.
You can. What took me a while to learn is that a file being correct and the desk doing what you meant are two different things, and nothing in the file tells you which one you have.
The mix that loaded, except for the part that mattered
Changing over between bands, I wanted to carry one player’s in-ear mix into the next scene: the bus strip plus every send feeding it. The mix came back wrong. The bus was panned right where it should have been, its tap was correct, and its level was somebody else’s.
I had three explanations before I had a single measurement. The channel was safed on the desk. An OSC message got dropped. Scene recall scope excluded it. Each one was plausible, each one would have explained the symptom, and each one was wrong.
What settled it was dumping the console’s live state back to a file and diffing it against the one I had sent. One line differed. The bus was stereo-linked to its neighbour, the write had touched only one side of the pair, and on recall the link reconciled the pair by pulling the written side back to match the side that hadn’t been touched.
That is finding number one, and it generalises.
Writing one side of a stereo-linked pair is not durable. Set the odd bus, leave the even side alone, and the level reverts on recall. Pan and tap survive, because on a send line they live only on the odd-numbered line — which produces the exact signature seen above: pan and tap recall correctly, level does not, and only one bus is affected. Anything that copies or sets a send has to write both sides.
The rest of these came out of the same loop, run against hardware.
A bad token aborts the rest of the line
This is the one worth knowing about, because it fails silently and halfway.
If a token is in range but in the wrong format, the console rejects it and stops parsing that line. Fields before the bad token apply. Every field after it reverts to its previous value on recall.
The compressor ratio is the case that bites. It is an enum, and the single-digit steps carry one decimal:
1.1 1.3 1.5 2.0 2.5 3.0 4.0 5.0 7.0 10 20 100
Write 4 where the console wants 4.0 and the line is /ch/NN/dyn <on> <mode> <det> <env> <thr> <ratio> …, so on and thr take, and ratio, knee, makeup gain, attack, hold and release all
quietly revert. The channel is compressing, just not the way the file says.
The signature to recognise is: the first few fields of a line took and everything after one
particular field did not. That reads like a failed edit or a muted channel, and it is neither.
Matching the console’s exact string formatting matters more than getting the value right — two
fields that look interchangeable often are not. The low cut in /preamp is a bare integer (80);
an EQ frequency carries a decimal (80.0).
Gain and phantom are not on the channel
/ch/NN/preamp looks like the preamp and is not. It holds the channel’s digital trim, polarity
and low cut, and its ON/OFF tokens are polarity and HPF — not phantom.
The analog gain and +48V live at /headamp/NNN, indexed by physical input, not by channel:
| Index | Physical input |
|---|---|
000–031 | Local 1–32 |
032–079 | AES50-A 1–48 |
080–127 | AES50-B 1–48 |
/headamp/003 +19.0 OFF is +19 dB with phantom off, on local input 4.
The consequence is that re-patching a channel moves the channel and leaves its gain behind on the
old jack. Move sixteen inputs from local to a stage box and you have moved sixteen channels to
sixteen inputs sitting at whatever gain the last thing plugged into them wanted. Channel and head
amp are also not 1:1 in general: the source slot in /ch/NN/config resolves through the routing
banks first, and only then is the headamp index the source number minus one. Card, Aux and OFF
sources have no head amp at all.
Frequencies come back different, and that is fine
On load the console rewrites tokens into its own house style. Numeric columns get re-padded,
decimals get forced (+4.0 becomes +4.00), and frequencies snap to a fixed detent grid. Ask for
4k50 and read back 4k52. Ask for 3k00 and read back 2k99. A reverb decay of 2.10 came
back 2.11; a damping of 8k00 came back 8k34.
A value being accepted means it survived as the nearest detent, not that it round-trips character-identical. A snapped frequency is not a failed write, and treating it as one sends you looking for a bug that isn’t there.
The scene name comes from the filename
The #4.0# header carries a title. The desk ignores it, names the scene from its filename or
console slot, and overwrites the header on its next save. Setting it is still correct for a file
you intend to read back with a tool. It is not what shows up on the console.
Import and Load are not the same act
The desk reads a file two ways. From a USB stick, through the desk’s own Scenes, Library and Utility pages, it imports into the console’s own slots and recalls from there. Through X32-Edit, Import reads the file onto the computer and Load pushes the values across.
They are not equivalent. Watched on a live console, Library → Import + Load applied an effect preset to a slot while the desk’s own library slot stayed empty — X32-Edit never wrote it. Same for a routing preset. The Routing Load dialog also has a Recall Patching Scope panel with eight ticks, all on by default, five of which cover sections a routing preset does not carry; leave them ticked and X32-Edit may write defaults into them.
The one convenience: X32-Edit’s scene export matches the desk’s own state byte for byte apart from the title, so its files are usable as ground truth for token formats.
The check that passes about something it never looked at
Two properties are cheap to verify on a scene file. It round-trips — parse it, dump it, and get the input back byte for byte. And it diffs clean against its source in exactly the lines you meant to change.
Both are byte-level checks, and there are file shapes they cannot see.
Send lines have a parity rule. Odd-numbered buses carry five tokens — on, level, pan, tap, and a trailing zero. Even-numbered buses carry exactly two, on and level, and inherit pan and tap from the odd side. This is decided by parity rather than by stereo linking: in a real scene, all 256 odd-bus send lines carry five tokens and all 256 even-bus lines carry two, including on an unlinked pair.
Write five fields onto an even-bus line and you have produced a line no console has ever written. It round-trips perfectly. It diffs clean. Neither check knows the rule exists.
Which is why the only thing that actually settles a question about an X32 file is the loop that found the linked-pair bug in the first place: write the file, load it on the desk, save the live state back off the console, and diff the two. The diff shows what the console accepted, what it rewrote, and what it ignored. It turns “the monitor mix didn’t load” into “bus 11’s level differs and its pair is unlinked in the file,” which points at the cause instead of at three good guesses.
Wrong theories are cheap and confident. Diff first.
These findings and the format notes behind them are written up in x32scene, a Python tool for reading, diffing and editing X32 and M32 files. It is MIT licensed. The docs mark which facts were observed on hardware and which were read from a manual, because the difference turned out to matter.