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, the lighting rig —
and the window's own state such as which cue was selected. The rig is a
lighting section — fixtures, the types they are, groups, each universe's
outputs and the consoles heard — written only once a show has one, so a show with no
lights saves the bytes it always did. Which network interface the lights leave by,
and the sACN identity this machine sends as, are the machine's, in
dmx.json in the user-data folder.
A fixture type read from a GDTF file (DIN SPEC 15800; Lighting →
Patch → Import GDTF file…) is copied into the show like any
other type, so the show does not need the file again. Only the archive's
description.xml is read — the models and wheel images beside it are
not — up to 16 MB of XML, and a description that declares a document type is
refused. Each DMXMode becomes a mode, as wide as its highest offset. Each
DMXChannel with an offset becomes a parameter: its kind from the
attribute of the function it starts on (Dimmer, Pan,
Tilt, ColorAdd_R and the other emitters, colour wheels and
macros as colour, everything else as other), its label from the attribute's pretty
name — with the geometry after it where cells share an attribute — its coarse and
fine slots from Offset, and where it rests from the default of its
initial function (or, in a GDTF 1.0 file, the channel's own Default).
A channel with more than one ChannelFunction keeps each one's name and
the share of travel it starts at (DMXFrom), up to 64, and the faders name
the one a value is in. A 24- or 32-bit channel is driven on its two most significant
bytes and the bytes below are held at zero. A virtual channel (no offset) is left out.
Refused, with a sentence: a mode spread over more than one DMXBreak, a
channel that overwrites another break, an offset past 512, two channels on one slot,
a mode with no DMX channels, more than 32 modes or 128 parameters to a mode.
A show with more than one cue list carries a cueLists section: each list's
id, name, colour, whether it is a cart, whether it is disarmed, how it follows the
clock and its tiles' own keys, in tab order. A cue at the top level of a list other than
the main one names it with listId; a cue inside a group never does — it runs
in its group's list. A show with only its main list under its own name writes no
section and no listId, so a show saved before lists existed opens as one
list and saves the bytes it always did. A cue naming a list the file does not have runs
in the main list, and the bar across the top says so, the way a grouping repair does.
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.
Video and image cues store their files the same way, and move with the show the
same way. The show's outputs are a videoOutputs list — name, size, timer,
corner pin and soft edges, a mirror and a mask — written only when the show has one;
where each output goes on a computer is kept on that computer, in
video-outputs.json in the user-data folder. A show whose pictures go on the next
GO carries a pictures section; a camera cue carries its camera's name, and
each computer keeps the device it picked for that cue in camera-devices.json.
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.
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 file
A .cuerunnerx is the show and its media as one file — the audio,
video, image and MIDI files its cues play — 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.
QLab workspaces
Import QLab Workspace… reads a .qlab5 or
.qlab4 workspace, a QLab 5 workspace saved as a folder (the property list
inside it), a QLab 5 backup (.qlab5backup, the same archive; in its
backups folder its audio is looked for from the folder above, where the
workspace was), and a .cues file of cues with no list around them. A
workspace is an NSKeyedArchiver archive in a binary property list, with its cue lists
stored as a second archive inside the first; both are Apple's container formats, read
by Cuerunner's own reader. QLab 5 archives each cue list as a Group cue of mode 0 (a
cart, mode 5) under a root group, and each becomes a cue list rather than a group.
An audio cue's slices are its AudioSlice markers, each the end of a slice,
and its lastSlice; a marker past 127 is not carried. A workspace past 64 MB is refused before it is
read, and so is one holding more than 10,000 cues.
From a running QLab, the import connects over TCP to the address and port given
(53000 by default), SLIP-framed as QLab's OSC over TCP is, and sends
/workspaces, /workspace/{id}/connect with the passcode,
/workspace/{id}/cueLists, and /workspace/{id}/cue_id/{id}/valuesForKeys
once for each cue — twice for an audio or devamp cue, whose slice markers and devamp
settings are asked for on their own so a QLab that does not know them still gives the
rest. One message is in flight at a time. The connection has 3 seconds
to open and every reply 5 seconds to arrive; a reply larger than 16 MB, or a
conversation past 64 MB, ends the import.
The result is a new show with no file, checked by the same reading every
.cuerunner file goes through. Audio paths are absolute: the QLab
workspace's folder joined to the path QLab recorded relative to it, or QLab's last
known absolute path where there is none. Find in Folder… reads
each folder it looks in to at most 10,000 entries, once, for a file whose name differs
only by ' ’ : / ? " < > | * \ written as _ or by its
Unicode form, and takes it only when exactly one does. The mapping is in
Coming from QLab.
| The import says | Because |
|---|---|
| … is not a QLab workspace | The file is not a binary property list, which every QLab workspace is. |
| … looks like a QLab workspace but cannot be read | It is a property list whose bytes contradict themselves, or not an archive; the rest of the sentence says which. |
| … holds no QLab cue lists | An archive, but not of a QLab show. |
| … is N MB, and a QLab workspace this import reads is at most 64 MB | The size limit, checked before the file is read. |
| no QLab answered at … | Nothing is listening at that address and port, or QLab's OSC access does not let this computer in. |
| … did not accept that passcode | The workspace's passcode is not the one typed. |
| … stopped answering | A reply did not arrive within 5 seconds. |
Wireless telemetry
Apart from Sennheiser EW-DX receivers, which Cuerunner reads directly, it 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 }
]
}| Field | Meaning |
|---|---|
id | Stable identifier for the channel. Required. |
name | What the receiver calls it. |
batteryPercent | 0–100. |
batteryMinutes | Runtime left, if the receiver reports it. |
rfDbm | Signal strength, negative dBm. |
rfWeak | Whether the receiver itself calls the signal weak. When it is present it replaces the Signal below line for that channel, and it goes with rfDbm. |
audioLevel | 0–100. |
muted | Whether the channel is muted. |
warnings | What the receiver itself says is wrong, as a list of { "text": "audio clipping", "critical": false }. Each is announced once when it appears and shown under the pack; critical marks one that makes the pack unusable until it is dealt with. |
frequencyMhz | The 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.