SEB scenes and Event Player in FW 0.12.11
Use Berry SEB.land and SEB.emit in firmware 0.12.11 for causal scenes and timed Event-value segments.
What SEB is
Section titled “What SEB is”SEB (Spectoda Event Batch) is a small binary file containing discrete Event values/records. Each record contains a native value, label, Spectoda ID, priority and an absolute time offset inside the segment. Landing or emitting a record produces normal EventStateUpdate processing through EventStore.
SEB is not a Player authoring or Project format. It contains no scene name,
Track name, loop, or seek rules. Project Berry owns those policies. The static
player.show v3 file is a compact manifest for one global mixed-ID stream of
fixed player.NNN NetworkStorage slots.
One SEB segment contains at most 340 records, occupies at most 4,092 bytes and
lasts at most 65,535 ms. This fills one 4,096-byte LittleFS data-block budget;
filesystem metadata is stored separately. The runtime still accepts at most
340 due events in one atomic contribution, so one maximum same-time Cue Group
across all IDs can commit atomically. A 341st pending event fails before any
queue, EventStore, or cursor mutation. A Cue Group is never split. Long shows
are ordered collections of bounded segments.
Firmware owns the normative byte layout and validation rules in
components/spectoda-library/docs/SEB_V1.md; this is the
exact FW 0.12.11 review revision.
SEB.land and SEB.emit
Section titled “SEB.land and SEB.emit”Use SEB.land on every prepared controller for a distributed Player. Events
enter each local EventStore with the same Network time without an immediate
execute-bytecode broadcast. Normal EventStore synchronization can later bring
a controller that missed the landing up to date.
Use SEB.emit only when exactly one authoritative source exists. Calling it on
every controller would multiply network event traffic.
The scalar API remains available in FW 0.12.11:
spectoda.landEvent(label, value, ids, vtype)spectoda.landEvent(label, value, ids, vtype, event_millis)spectoda.emitEvent(label, value, ids, vtype)spectoda.emitEvent(label, value, ids, vtype, event_millis)ids may be one ID, a list of IDs, or nil for broadcast. Optional
event_millis preserves the exact causal Controller-local time of the source
event. Use the SEB component for batches; it is not a plural overload of the
scalar method.
var result = SEB.land("segment-000.seb", { "source": "networkStorage", "at": segment_at, "cursor": cursor, "until": position})
if result["ok"] cursor = result["cursor"]else print("SEB error", result["error"])endat, cursor and until
Section titled “at, cursor and until”atis the wrapping localcontroller.millis()token corresponding to offset zero. Timed segments require it.cursoris the first source record that has not been consumed yet. It defaults to0.untilis the inclusive due offset. It defaults to0.source: "networkStorage"makes the first argument a filename. Without it, the first argument remains a Berrybytesvalue.
One call consumes the maximal due prefix. It validates and reserves the whole contribution before applying it atomically. A failure leaves EventStore, queues and cursor unchanged, so Berry can retry safely.
One args.at contract covers three explicit time domains:
timeline.at(segmentStart)projects a Player timeline position to a local wrapping millis token;event_millisfrom anEVS.cbtrigger preserves an event’s Network-clock causality for scene recall;controller.millis()means now on this Controller.
Network clock remains the EventStore ordering authority. SEB only accepts the local token and has no timeline-specific overload.
SEB retains no Berry pointer, filename, file handle, cursor or Player state.
The Project must keep at bit-identical for the exact source/cursor pair. It
resets or replaces the cursor when changing
segment, seeking, looping or changing the timing projection.
The reference Player handles pause/resume as explicit outer policy. It derives
a fresh filename/at pair with the new projection and carries only the known
first-unconsumed source index. Do not reuse that number with a different source
or an arbitrary at.
Causal scene recall
Section titled “Causal scene recall”A scene is an ordinary mixed-ID SEB with duration_ms = 0 and all offsets at
zero. A trigger Event value can carry the scene label. Its EVS.cb callback
also receives the exact local time of the source event; pass that value as
at.
var scene = EVS("scene", ID255)
scene.cb = def(value, event_id, event_millis) if event_millis == nil || !value.is(31) return end
var name = value.get(31) + ".seb" var result = SEB.land(name, { "source": "networkStorage", "at": event_millis }) if !result["ok"] print("Invalid scene", result["error"]) endendDo not compile the $scene[ID255] trigger itself into the scene file, or the
recall would recurse.
Scene recall never calls timeline.at(). It stays causal while the timeline is
paused, because the callback’s event_millis came from the trigger event.
player.show v3
Section titled “player.show v3”The copy-ready Player reads player.show and polls timeline.getState().
The exact-EOF manifest has a 12-byte header followed by one 10-byte entry for
each fixed segment slot:
- exact required NetworkStorage version as
u48little-endian; - absolute segment start on the show timeline as
u32little-endian.
Entry index 0 means player.000, index 1 means player.001, and so on up
to player.999. Names are not stored. Berry obtains each local version from
spectoda.listNetworkStorageData() as a fixed-width lowercase 12-character
hexadecimal string and compares it exactly without converting it to a 32-bit
number.
Studio publishes a newer manifest first and changed slots afterwards. This is not a hot-update protocol: a Controller stays inactive until every requested version is present and every SEB header/EOF validates. Ordinary NetworkStorage synchronization supplies missing files. Upload never changes timeline state.
Timeline, pause and seek
Section titled “Timeline, pause and seek”Normal forward playback selects the current segment from its absolute start,
calculates at through timeline.at(segmentStart), and passes the current
relative position as until. Every Controller lands every ID from the same
stream. An ordinary pause without a timeline discontinuity makes no SEB call.
timeline.at(position) records that the current timeline reference position
P was valid at Network clock C, projects C + (position - P), and converts
that clock to this Controller’s signed wrapping local token. The same formula
is used while running and paused, so Controllers with different command
processing latency still resolve the same Network timestamp. The call returns
nil when the target clock is invalid or outside the unambiguous ±2^31 ms
wrapping window.
Timeline position is a separate 24-hour ring, 0..86,399,999; 86,400,000
normalizes to 0. Midnight therefore remains continuous for the Player,
Layers and animations. Projection selects the nearest occurrence within ±12
hours; an exact 12-hour delta is ambiguous and returns nil.
Pause/resume keeps the timeline epoch but resume creates a new causal
projection; the Player derives a fresh at for the current segment while
retaining its already-consumed source prefix. Seek and loop change the epoch
and force cursor positioning again.
On seek, loop, rewind, or a 24-hour wrap, the Player selects the exact-target Cue Group or the immediately preceding Group. It lands that one sparse Group once at the causal seek time, including while paused, and advances the cursor past it. Resume therefore does not repeat it and no future Cue is selected.
This is sparse reconciliation, not complete-state reconstruction. The Player does not clear EventStore or replay older history. Events absent from the selected Cue keep their existing EventStates. Use a complete Cue where an arbitrary seek must recreate a complete visual state.
EventStore remains authoritative
Section titled “EventStore remains authoritative”SEB never mutates EventState behind EventStore. The latest EventStateUpdate therefore remains authoritative after historical synchronization, and an externally emitted event can change Player state through the normal path.
Copy-ready examples
Section titled “Copy-ready examples”The examples use synthetic IDs and exact NetworkStorage artifacts for FW 0.12.11. To author a show in Studio, continue to Event Player in Spectoda Studio.