instance_token is the producer's own AutomationProgram::instance_token() carried verbatim, because lane identity is not (lane_id, generation) alone. More...
#include <program_wire.hpp>
Public Attributes | |
| std::uint64_t | lane_id = 0 |
| std::uint64_t | generation = 0 |
| std::uint64_t | instance_token = 0 |
| Nonzero always. | |
| std::uint32_t | segment_first = 0 |
| std::uint32_t | segment_count = 0 |
| float | leading_value = 0.0f |
| std::uint8_t | target_kind = 0 |
| std::uint8_t | evaluation_rate = 0 |
| std::uint16_t | pad0 = 0 |
| std::uint64_t | device_placement_id = 0 |
| Zero unless target_kind is DeviceParameter. | |
| std::uint32_t | device_param_id = 0 |
| Zero unless target_kind is DeviceParameter. | |
| std::uint8_t | mixer_parameter = 0 |
| Zero unless target_kind is TrackMixer. | |
| std::uint8_t | pad1 [3] = {} |
instance_token is the producer's own AutomationProgram::instance_token() carried verbatim, because lane identity is not (lane_id, generation) alone.
In-process, AutomationCursor decides Unchanged on the lane key and the token together, so the token is what stops two different programs that share a key from being mistaken for one; a consumer computing Unchanged without it reaches the opposite answer to the cursor and renders a stale lane, silently, with no decode error to notice.
The objection this answers is that the token is a producer-process-local allocation counter, so a foreign one names nothing a consumer can look up. True, and beside the point: a consumer never compares a foreign token to one of its own. It compares two foreign tokens to each other, within one producer_epoch, where they came from the same counter and mean exactly what they mean in process. Across epochs they are indeed incomparable — and across epochs the epoch has already decided, so the token is not consulted.
Equality only. A larger token does not mean newer on the wire: ordering is (producer_epoch, generation)'s job, and a producer is free to publish programs compiled out of order. Zero is refused for the reason a zero epoch is — it would compare equal to every other zero and quietly merge two distinct programs into one — but at the decoder only, not at both ends like the epoch: AutomationProgram's constructor is private to the compiler, which always mints a nonzero token, so an encoder-side check would be unreachable. The decoder is where a foreign payload arrives, which is the end that needs it.
The incremental compiler reuses a lane's program when that lane did not change, so its token is stable across a publish that touched only its neighbours. Per-lane rather than per-publication is what preserves that: a consumer re-adopts the lanes that actually moved and keeps its cursor state for the rest.
| std::uint32_t pulp::playback::ProgramWireAutomationLaneRecord::device_param_id = 0 |
Zero unless target_kind is DeviceParameter.
| std::uint64_t pulp::playback::ProgramWireAutomationLaneRecord::device_placement_id = 0 |
Zero unless target_kind is DeviceParameter.
| std::uint8_t pulp::playback::ProgramWireAutomationLaneRecord::evaluation_rate = 0 |
| std::uint64_t pulp::playback::ProgramWireAutomationLaneRecord::generation = 0 |
| std::uint64_t pulp::playback::ProgramWireAutomationLaneRecord::instance_token = 0 |
Nonzero always.
Comparable only against another token carrying the same producer_epoch, and only for equality.
| std::uint64_t pulp::playback::ProgramWireAutomationLaneRecord::lane_id = 0 |
| float pulp::playback::ProgramWireAutomationLaneRecord::leading_value = 0.0f |
| std::uint8_t pulp::playback::ProgramWireAutomationLaneRecord::mixer_parameter = 0 |
Zero unless target_kind is TrackMixer.
| std::uint16_t pulp::playback::ProgramWireAutomationLaneRecord::pad0 = 0 |
| std::uint8_t pulp::playback::ProgramWireAutomationLaneRecord::pad1[3] = {} |
| std::uint32_t pulp::playback::ProgramWireAutomationLaneRecord::segment_count = 0 |
| std::uint32_t pulp::playback::ProgramWireAutomationLaneRecord::segment_first = 0 |
| std::uint8_t pulp::playback::ProgramWireAutomationLaneRecord::target_kind = 0 |