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 complete Player or Project format. It contains no scene name,
Track name, loop or seek rules. Project Berry owns those policies. The static
player.show file is the bounded directory for deterministic per-ID SEB
chains stored in NetworkStorage.
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 complete maximum Cue can
commit atomically. A 341st pending event fails before any queue, EventStore or
cursor mutation. A long Track is an ordered collection 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.
Timeline, pause and seek
Section titled “Timeline, pause and seek”The small copy-ready Player reads <base>.show and polls
timeline.getState(). While paused is true it makes no SEB call. Normal
forward playback selects the next complete Cue from a per-ID segment,
calculates at through timeline.at(segmentStart) and passes
the current relative position as until.
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 reopens the remaining segment timing while retaining its already-consumed source prefix. Seek and loop change the epoch. Old timing state and cursors must then be discarded and rebuilt from a complete checkpoint.
Seeking is not a replay of every skipped event. The static player.show
directory lets Project Berry locate the last complete Cue at or before the
target independently for every relevant Track. It lands those Cue values at
the seek time, then continues after them. Skipped historical side effects are
not fired again.
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.