Loading...
Searching...
No Matches
Rendezvous.hpp
1#pragma once
2
3#include <ossia/dataflow/value_port.hpp>
4#include <ossia/network/value/value.hpp>
5
6#include <halp/callback.hpp>
7#include <halp/controls.hpp>
8#include <halp/dynamic_port.hpp>
9#include <halp/meta.hpp>
10
11#include <algorithm>
12#include <functional>
13#include <utility>
14#include <vector>
15
16namespace avnd_tools
17{
19{
20 halp_meta(name, "Rendezvous")
21 halp_meta(c_name, "avnd_rendezvous")
22 halp_meta(author, "ossia team")
23 halp_meta(category, "Control/Mappings")
24 halp_meta(description, "Emit an ordered list when every input has a fresh event")
25 halp_meta(uuid, "ec5529c6-4c69-47bd-9eb7-3bfbc3cc5ccf")
26
27 // Retain native events so First also means the first of multiple arrivals
28 // within one processing tick, including events with equal timestamps.
29 struct Input
30 {
31 halp_meta(name, "Input {}")
32 ossia::value_port* value{};
33 static constexpr bool is_event() { return true; }
34 };
35
36 struct
37 {
38 struct : halp::spinbox_i32<"Input count", halp::range{0, 512, 2}>
39 {
40 static std::function<void(Rendezvous&, int)> on_controller_interaction()
41 {
42 return [](Rendezvous& object, int value) {
43 object.inputs.in_i.request_port_resize(std::clamp(value, 0, 512));
44 };
45 }
46
47 void update(Rendezvous& object)
48 {
49 const int count = std::clamp(this->value, 0, 512);
50 if(count != object.requested_count)
51 {
52 object.requested_count = count;
53 object.clear();
54 }
55 }
56 } controller;
57
58 struct : halp::toggle<"Keep first">
59 {
60 halp_meta(
61 description,
62 "Keep the first fresh value on each input until the cycle completes. "
63 "When disabled, keep the latest value. Changes affect future arrivals.")
64 } keep_first;
65
66 halp::dynamic_port<Input> in_i;
67 halp::impulse_button<"Clear"> clear;
68 } inputs;
69
70 struct
71 {
72 halp::callback<"Output", const std::vector<ossia::value>&> out;
73 // Number of inlets still missing a fresh event, not a count of messages.
74 halp::val_port<"Waiting", int> waiting;
75 } outputs;
76
77 void operator()()
78 {
79 const auto count = inputs.in_i.ports.size();
80 if(pending.size() != count)
81 {
82 pending.resize(count);
83 received.resize(count);
84 reset_cycle();
85 }
86
87 // Clear wins over arrivals in the same tick and is consumed only here.
88 if(inputs.clear.value)
89 {
90 inputs.clear.value.reset();
91 clear();
92 return;
93 }
94
95 for(std::size_t i = 0; i < count; ++i)
96 {
97 const auto* input = inputs.in_i.ports[i].value;
98 if(!input || input->get_data().empty())
99 continue;
100 if(!received[i] || !inputs.keep_first.value)
101 {
102 const auto& arrivals = input->get_data();
103 pending[i]
104 = inputs.keep_first.value ? arrivals.front().value : arrivals.back().value;
105 }
106 if(!received[i])
107 {
108 received[i] = true;
109 ++ready_count;
110 }
111 }
112
113 outputs.waiting.value = static_cast<int>(count - ready_count);
114 if(count != 0 && ready_count == count)
115 {
116 // Publish during processing, never from a UI/update callback, because
117 // the host clears graph output ports before invoking operator().
118 outputs.out(pending);
119 reset_cycle();
120 }
121 }
122
123private:
124 int requested_count = 2;
125 std::vector<ossia::value> pending;
126 std::vector<bool> received;
127 std::size_t ready_count{};
128
129 void reset_cycle()
130 {
131 for(auto& value : pending)
132 value = ossia::value{};
133 std::fill(received.begin(), received.end(), false);
134 ready_count = 0;
135 outputs.waiting.value = static_cast<int>(inputs.in_i.ports.size());
136 }
137
138 void clear() { reset_cycle(); }
139};
140}
Definition Rendezvous.hpp:30
Definition Rendezvous.hpp:19