Somnus
Somnus runs the show.
Everything else here takes orders. Somnus gives them.
Hand it an audio track and a list of cues. As the playhead moves it fires those cues at the hardware. Poppers at 4:12. The socket live from 6:30 to 9:00. The track is the clock and nothing on the rig drifts off it.
Source on git.somnus.engineering
The audio is the timeline
Somnus plays the track itself rather than asking something else to and hoping. It counts samples, so it always knows exactly where it is, and it can be dropped anywhere in the track without guessing.
Cues sit at absolute times on that track. Write a session once, run it as many times as you like, and it lands the same way every time.
A cue has a length, not just a moment
Some things happen. Some things go on happening.
A cue with a duration gets its start and its stop from Somnus as one piece. You say the socket is live for two and a half minutes. Somnus fires the start, then fires the stop when the time is up, and holds itself to that.
Scrub the timeline, pause, or stop early, and it settles those stops itself. Nothing is left running because a scene ended sooner than planned.
Stop is not a request
STOP halts the audio and walks every device back to its safe state.
PANIC sits on the screen at all times and does the same thing without waiting to be polite about it. Both go down the same path, and every device plugin has to implement it. Anything that cannot be put into a safe state does not get to be a device.
Pause is a safe pause. Whatever is still running gets stopped, then started again when you resume. Seeking drops every device to safe state first and only then fires forward from wherever you landed.
The subject does not get a vote on any of this. Whoever is holding PANIC does.
A dead device does not stop the show
Devices connect when the show is armed. If one is missing, unplugged, or simply refuses, Somnus says so on screen and plays anyway. Its cues do nothing, and they get logged as having done nothing.
Every device gets its own goroutine and its own ordered queue, so devices run alongside each other and never out of order among themselves. Every call into a plugin runs under a timeout with a panic recovery around it. Hardware that hangs or falls over cannot stall the audio and cannot drag the rest of the rig down with it.
Anything can be a device
A plugin declares what it can do and what each action takes: switches, numbers with limits on them, durations, lists to pick from. The editor reads that and builds the form. Nothing about any particular device is written into Somnus itself, so teaching it new hardware is writing a plugin, not editing the engine.
Outlet and Milker are both wired in already, and neither is a write and walk away. Somnus holds them by talking to them continuously, and the moment that stops they stop themselves. Kill Somnus mid-show and the mains drops inside a second, the machine inside half of one, whether or not anything got the chance to tidy up after itself.
Adding a device does not mean copying a serial number out of a terminal either. Somnus scans for what is plugged in and hands you the list, already marked with which ones something else has claimed. It confirms each one by listening to it rather than believing what the USB descriptor says, because an Outlet and a Milker are the same bridge chip as far as the port is concerned, and offering a stroking machine where you asked for a mains relay is not a mistake worth leaving possible.
Drive it from across the room
One binary. It plays the audio, runs the engine, holds the plugins, and serves the interface. Point a browser on the same network at it and that browser is a remote.
The operator page has the transport, live device health, the event feed, and PANIC. The editor page has the waveform, cues you can drag, forms built out of whatever the plugins said they could do, and a test fire so you can prove a device works before anyone is attached to it.
Every run gets written down, timestamped, and kept.