Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
motion_scrubber.hpp
Go to the documentation of this file.
1// motion_scrubber.hpp — Timeline scrubber that replays a .motion.jsonl
2// fixture frame-by-frame against an inspector client.
3//
4// The scrubber is passive: it does not run animations live and does not
5// advance a clock. It loads a recorded fixture into memory, exposes a
6// playhead (frame index), and re-emits the prefix of events with
7// `frame <= playhead` to a caller-supplied Sink each time the playhead
8// moves. This is sufficient for design-review timeline scrubbing and CI
9// artifact triage; live overlay drawing belongs to a separate layer.
10//
11// Inspector dispatch:
12// Motion.loadFixture { path } → load events, reset playhead to 0
13// Motion.scrubTo { frame } → set playhead, re-emit prefix
14// Motion.play {} → mark playing, jump to max frame
15// Motion.pause {} → clear playing flag
16// Motion.snapshot {} → playhead + loaded state
17//
18// The scrubber is off by default — `loaded()` is false until
19// `load_fixture()` succeeds. Wiring is analogous to MotionInspector:
20// DomainHandler owns the instance and routes Motion.* requests to it
21// when the method is a scrubber method.
22
23#pragma once
24
26#include <pulp/view/motion.hpp>
27
28#include <cstdint>
29#include <mutex>
30#include <string>
31#include <vector>
32
33namespace pulp::inspect {
34
36public:
40 explicit MotionScrubber(InspectorEventSink event_sink = {});
42
45
50
54 static bool owns_method(const std::string& method);
55
56 // ── Direct API (used by tests + advanced clients) ───────────────
57
61 bool load_fixture(const std::string& path);
62
67 std::size_t scrub_to(std::uint64_t frame);
68
71 std::size_t scrub_to_bounded(std::uint64_t frame, std::size_t maximum_events,
72 bool& truncated, std::function<bool()> continue_emission = {},
73 bool* interrupted = nullptr);
74
77 std::size_t play();
78
79 std::size_t play_bounded(std::size_t maximum_events, bool& truncated,
80 std::function<bool()> continue_emission = {},
81 bool* interrupted = nullptr);
82
84 void pause();
85
89 void remove_sink(int id);
90
91 // ── Read-only accessors ─────────────────────────────────────────
92
93 bool loaded() const;
94 bool playing() const;
95 std::uint64_t playhead_frame() const;
96 std::size_t event_count() const;
97 std::uint64_t max_frame() const;
99
100private:
101 InspectorEventSink event_sink_;
102
103 mutable std::mutex mtx_;
104 std::vector<view::motion::SampleEvent> events_;
106 std::uint64_t playhead_frame_ = 0;
107 std::uint64_t max_frame_ = 0;
108 bool loaded_ = false;
109 bool playing_ = false;
110
111 struct SinkSlot {
112 int id = 0;
114 };
115 std::vector<SinkSlot> sinks_;
116 int next_sink_id_ = 1;
117
118 // Protocol handlers.
119 InspectorMessage handle_load_fixture(const InspectorMessage& req);
120 InspectorMessage handle_scrub_to(const InspectorMessage& req);
121 InspectorMessage handle_play(const InspectorMessage& req);
122 InspectorMessage handle_pause(const InspectorMessage& req);
123
124 // Dispatch a snapshot of events to the given sinks + callback WITHOUT
125 // holding mtx_. Callers must collect the snapshot under the lock and
126 // then call this. Sinks are free to re-enter MotionScrubber methods.
127 static std::size_t dispatch_snapshot(
128 const std::vector<view::motion::SampleEvent>& events,
129 const std::vector<SinkSlot>& sinks,
130 const InspectorEventSink& event_sink,
131 const std::function<bool()>& continue_emission = {}, bool* interrupted = nullptr);
132};
133
134} // namespace pulp::inspect
std::size_t play_bounded(std::size_t maximum_events, bool &truncated, std::function< bool()> continue_emission={}, bool *interrupted=nullptr)
MotionScrubber(const MotionScrubber &)=delete
InspectorMessage handle(const InspectorMessage &req)
Handle a Motion.
std::size_t play()
Set playing flag, advance to the last loaded frame, emit everything.
std::size_t event_count() const
std::size_t scrub_to(std::uint64_t frame)
Move the playhead to the given frame and re-emit the prefix of events with frame <= playhead.
std::size_t scrub_to_bounded(std::uint64_t frame, std::size_t maximum_events, bool &truncated, std::function< bool()> continue_emission={}, bool *interrupted=nullptr)
Canonical control variant: emits at most maximum_events from the selected prefix and reports whether ...
MotionScrubber(InspectorEventSink event_sink={})
Construct with an optional event delivery callback.
int add_sink(view::motion::Sink sink)
Install a sink that receives re-emitted events.
bool load_fixture(const std::string &path)
Load a fixture file.
std::uint64_t playhead_frame() const
MotionScrubber & operator=(const MotionScrubber &)=delete
static bool owns_method(const std::string &method)
True when at least one of the scrubber methods is the natural target for req.method.
view::motion::FixtureHeader header() const
std::uint64_t max_frame() const
void pause()
Clear the playing flag. No event emission. Playhead unchanged.
Agent-first motion observability: sample view geometry, scroll state, and arbitrary scalar values at ...
@ path
Definition scene_node.hpp:93
Definition child_process_manager.hpp:18
Definition agent_context.hpp:7
std::function< void(const InspectorMessage &)> InspectorEventSink
Definition inspector_delivery.hpp:30
std::function< void(const SampleEvent &)> Sink
Definition motion.hpp:389
A single inspector protocol message (request, response, or event).
Definition protocol.hpp:18
Header metadata recorded once at the top of a fixture file.
Definition motion.hpp:257