23 halp_meta(name,
"Rate Limiter")
24 halp_meta(c_name,
"RateLimiter")
25 halp_meta(category,
"Control/Mappings")
27 manual_url,
"https://ossia.io/score-docs/processes/rate-limiter.html#rate-limiter")
28 halp_meta(author,
"ossia score")
31 "Limit and quantize a value stream, or debounce after quiet milliseconds "
32 "(debounce ignores quantization)")
33 halp_meta(uuid,
"76cfd504-7c10-4bdb-a1b4-fbe449cc06f0")
45 struct : halp::hslider_i32<
"Duration", halp::irange{0, 1000, 10}>
49 "Minimum interval in milliseconds, or quiet time before a debounced value")
51 struct : halp::enum_t<Mode,
"Mode">
53 void update(
Node& self)
55 if(self.previous_mode != this->value)
62 halp::timed_callback<
"out", ossia::value> out;
68 using tick = ossia::token_request;
69 ossia::exec_state_facade ossia_state;
71 static constexpr int64_t flicks_per_ms = 705'600;
73 std::optional<ossia::value> pending;
74 long double deadline{};
75 std::optional<int64_t> previous_end;
76 Mode previous_mode{Mode::Limit};
77 ossia::small_vector<const ossia::timed_value*, 16> ordered_events;
84 previous_mode = inputs.mode.value;
87 void prepare(halp::setup)
noexcept { reset(); }
88 void start() noexcept { reset(); }
89 void stop() noexcept { reset(); }
90 void transport(
auto) noexcept { reset(); }
92 bool should_output(
float quantif, int64_t ms,
const tick& t)
96 else if(t.date.impl >= (last_time + ms * flicks_per_ms))
98 last_time = t.date.impl;
104 void operator()(
const tick& t)
106 if(previous_mode != inputs.mode.value
107 || (previous_end && t.prev_date.impl != *previous_end))
109 previous_end = t.date.impl;
111 const auto [start, frames] = ossia_state.timings(t);
112 if(inputs.mode.value == Mode::Debounce)
115 if(t.date <= t.prev_date || t.speed <= 0.)
122 debounce(t, start, frames);
126 auto quantif = inputs.quantification.value;
127 auto ms = inputs.ms.value;
128 for(
const ossia::timed_value& v : inputs.port.value->get_data())
130 if(v.timestamp < start || v.timestamp >= start + frames)
134 if(should_output(quantif, ms, t))
135 outputs.out(v.timestamp - start, v.value);
139 for(
const auto& point : t.get_quantification_dates(1. / quantif))
142 t.physical_position(point.position, ossia_state.modelToSamples()),
150 void debounce(
const tick& t, int64_t start, int64_t frames)
155 const long double begin = t.prev_date.impl;
156 const long double duration =
static_cast<long double>(t.date.impl) - begin;
157 const int64_t delay = std::max(0, inputs.ms.value) * flicks_per_ms;
158 const auto emit = [&] {
162 = std::max(0.L, std::ceil((deadline - begin) * frames / duration));
165 outputs.out(
static_cast<int64_t
>(frame), std::move(*pending));
169 const auto event = [&](
const ossia::timed_value& v) {
170 if(v.timestamp < start || v.timestamp >= start + frames)
172 const auto date = begin + (v.timestamp - start) * duration / frames;
175 if(pending && deadline < date)
180 outputs.out(v.timestamp - start, v.value);
185 deadline = date + delay;
189 const auto& data = inputs.port.value->get_data();
190 if(std::is_sorted(data.begin(), data.end(), [](
const auto& a,
const auto& b) {
191 return a.timestamp < b.timestamp;
194 for(
const auto& v : data)
201 ordered_events.clear();
202 for(
const auto& v : data)
203 ordered_events.push_back(&v);
205 ordered_events.begin(), ordered_events.end(),
206 [](
const auto* a,
const auto* b) {
207 return a->timestamp < b->timestamp || (a->timestamp == b->timestamp && a < b);
209 for(
const auto* v : ordered_events)
212 if(pending && deadline < t.date.impl)
218 halp_meta(layout, halp::layouts::hbox)
219 halp_meta(background, halp::colors::background_mid)
220 halp::control<&ins::quantification> q;
221 halp::control<&ins::ms> t;
222 halp::control<&ins::mode> mode;