File formats

The workspace file

A show is one .cuerunner file: JSON, holding the cues in order, the show's setup — the desk, the cast, the scenes, the mic plot, the triggers — and the window's own state such as which cue was selected.

It does not hold the audio. Each audio cue stores the path to its file. Moving a show to another machine means taking the sound with it and keeping the paths valid: the simplest arrangement is a folder with the .cuerunner file and the audio beside it, copied whole.

A cue whose file has gone says missing where its length should be, and a bar across the top of the window offers to relink it — point it at the file once and the rest of the cues in the same folder follow.

If a file's cue grouping is damaged, it is repaired as the show opens rather than loaded as-is: a group's children are gathered into one block behind it, a cue whose group is not in the file keeps its place at the top level and fires with the show, and a loop of groups is broken so every cue in it returns to the top. A bar across the top of the window says how many repairs were made, and the list behind it names each one. The file on disk is unchanged until you save, which writes the repaired grouping.

Show files are not converted. The shape is declared and checked down to the fields, and a file this build does not know is refused — naming the file and the fact, with nothing on disk touched — rather than filled in. A settings section this build writes and the file leaves out is a refusal, and so is a field inside a section that this build does not write or that the file has not got. What a field carries is checked as well, but only for its JSON kind: a string where this build writes a number — or a number where it writes a list of strings, or an entry of such a list that is not what the list carries — is refused the same way, and null is read only where the show's own types allow it. A cue's own fields are read the same way, and the sentence names the cue: a MIDI cue's messages are a list, every message an object, a crosspoint's gain a number, and an OSC argument's value a number, a string or a boolean — a cue field carrying any other kind refuses, as does null where a cue's types allow none. What a cue carries that this build does not declare is kept rather than refused, because a cue on disk holds fields the runtime cue type does not: media, the route its file was found by. What a value says is the app's reading rather than the file's, so a name the code does not offer, a desk kind among them, is read as it stands. The one that catches a show saved before the scenes changed is inside the settings: the strips, the faders and the scenes are checked field by field, so a show whose channels carried a fader of their own, or whose scenes listed who was open rather than what rides what, is refused by name. Setting it up again takes minutes and no hand-editing: the channels and faders in Show Settings → Channels, and the placements in the Scenes grid, which is where they live now. A file refused over a value is the other case, and not one to set up again: the sentence names the field and both kinds, and since nothing here guesses what the value was meant to be, the file is corrected where that value sits or opened in the build that wrote it. Saving does not convert one either: the show on screen is written as it stands, and a save over a file this build cannot read keeps the history it had and replaces the file all the same.

The bundle filePro

A .cuerunnerx is the show and its audio as one file: a zip holding show.cuerunner and a media/ folder, with the showfile pointing inside it. It opens in any file manager, and its bytes are a function of the show, so two exports of the same show are the same file.

It carries up to about 4 GB of media — the zip format's own limit without its 64-bit extension, which Cuerunner does not write. A larger show is refused rather than written as an archive that misreports its own contents.

The file also carries a checksum of everything in it, and an import whose contents do not match is refused rather than half-unpacked. See Versions and handover.

Wireless telemetry

Cuerunner does not speak any receiver's own protocol. A source — an HTTP address or a file on disk — returns this document:

{ "channels": [ { "id": "ulxd-1", "name": "Pack 1", "batteryPercent": 42, "batteryMinutes": 95, "rfDbm": -62, "audioLevel": 71, "muted": false, "frequencyMhz": 606.5 } ] }
FieldMeaning
idStable identifier for the channel. Required.
nameWhat the receiver calls it.
batteryPercent0–100.
batteryMinutesRuntime left, if the receiver reports it.
rfDbmSignal strength, negative dBm.
audioLevel0–100.
mutedWhether the channel is muted.
frequencyMhzThe frequency it is on.

The field names are the contract: a bridge written against a receiver's own API only has to emit them. Any field a channel does not report is left out, and the panel shows nothing for a reading that was never made rather than a zero.