MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
InputNode.cpp
Go to the documentation of this file.
1#include "InputNode.hpp"
2
4
5// ─────────────────────────────────────────────────────────────────────────────
6// InputNode Implementation
7// ─────────────────────────────────────────────────────────────────────────────
8
10 : m_config(config)
11 , m_context(m_config.default_value, m_config.default_value,
12 Core::InputType::HID, 0)
13{
16}
17
18double InputNode::process_sample(double /*input*/)
19{
20 double target = m_target_value.load();
21 double current = m_current_value.load();
22
23 double output = apply_smoothing(target, current);
24 m_current_value.store(output);
25 m_last_output = output;
26
27 return output;
28}
29
30std::vector<double> InputNode::process_batch(unsigned int num_samples)
31{
32 std::vector<double> output(num_samples);
33 for (unsigned int i = 0; i < num_samples; ++i) {
34 output[i] = process_sample(0.0);
35 }
36 return output;
37}
38
40{
41 double extracted = extract_value(value);
42
43 double current = m_current_value.load();
44 double smoothed = apply_smoothing(extracted, current);
45
46 m_target_value.store(extracted);
47 m_current_value.store(smoothed);
48 m_has_new_input.store(true);
49
50 m_last_device_id.store(value.device_id);
51 m_last_source_type = value.source_type;
52
53 (void)m_input_history.push(value);
54
55 notify_tick(smoothed);
56}
57
59{
60 return m_has_new_input.exchange(false);
61}
62
63std::optional<Core::InputValue> InputNode::get_last_input() const
64{
65 auto history = m_input_history.snapshot();
66 if (history.empty())
67 return std::nullopt;
68 return history.back();
69}
70
71std::vector<Core::InputValue> InputNode::get_input_history() const
72{
73 return m_input_history.snapshot();
74}
75
77{
78 switch (value.type) {
80 return value.as_scalar();
81
83 const auto& vec = value.as_vector();
84 return vec.empty() ? m_config.default_value : vec[0];
85 }
86
88 // Default: treat data2 as 0-127 normalized value
89 const auto& midi = value.as_midi();
90 return static_cast<double>(midi.data2) / 127.0;
91 }
92
93 default:
95 }
96}
97
105
106double InputNode::apply_smoothing(double target, double current) const
107{
108 switch (m_config.smoothing) {
110 return target;
111
113 double diff = target - current;
114 return current + diff * m_config.smoothing_factor;
115 }
116
118 // y[n] = a * x[n] + (1-a) * y[n-1]
119 return m_config.smoothing_factor * target + (1.0 - m_config.smoothing_factor) * current;
120 }
121
122 case SmoothingMode::SLEW: {
123 double diff = target - current;
124 if (std::abs(diff) <= m_config.slew_rate) {
125 return target;
126 }
127 return current + (diff > 0 ? m_config.slew_rate : -m_config.slew_rate);
128 }
129
130 default:
131 return target;
132 }
133}
134
139
144
149
154
159
164
169
174
179
181{
182 return on_tick_if(
183 [min, max](const NodeContext& ctx) {
184 return ctx.value >= min && ctx.value <= max;
185 },
186 callback);
187}
188
191 InputEventType event_type,
192 std::optional<double> threshold,
193 std::optional<std::pair<double, double>> range,
194 std::optional<NodeCondition> condition)
195{
196 const auto tag = static_cast<uint8_t>(event_type);
197
198 if (condition) {
199 return m_input_callbacks.add_conditional(adapt_hook(callback), std::move(*condition), callback.target_type(), tag);
200 }
201
202 std::array<double, 2> params {};
203 if (range) {
204 params = { range->first, range->second };
205 } else if (threshold) {
206 params[0] = *threshold;
207 }
208 return m_input_callbacks.add(adapt_hook(callback), callback.target_type(), tag, params);
209}
210
212{
214 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
215 auto& ctx = static_cast<InputContext&>(get_last_context());
216
217 m_callbacks.fire(ctx);
219
220 for (const auto& entry : m_input_callbacks.snapshot()) {
221 bool should_fire = false;
222
223 switch (static_cast<InputEventType>(entry.tag)) {
225 should_fire = true;
226 break;
227
229 const double epsilon = entry.params[0];
230 should_fire = std::abs(value - m_previous_value) > epsilon;
231 break;
232 }
233
235 should_fire = (m_previous_value < entry.params[0] && value >= entry.params[0]);
236 break;
237
239 should_fire = (m_previous_value >= entry.params[0] && value < entry.params[0]);
240 break;
241
243 const double min = entry.params[0];
244 const double max = entry.params[1];
245 bool in_range_now = (value >= min && value <= max);
246 should_fire = (!m_was_in_range && in_range_now);
247 m_was_in_range = in_range_now;
248 break;
249 }
250
252 const double min = entry.params[0];
253 const double max = entry.params[1];
254 bool in_range_now = (value >= min && value <= max);
255 should_fire = (m_was_in_range && !in_range_now);
256 m_was_in_range = in_range_now;
257 break;
258 }
259
261 should_fire = (m_previous_value < 0.5 && value >= 0.5);
262 break;
263
265 should_fire = (m_previous_value >= 0.5 && value < 0.5);
266 break;
267
269 should_fire = entry.condition && entry.condition.value()(ctx);
270 break;
271 }
272
273 if (should_fire) {
274 entry.hook(ctx);
275 }
276 }
277
279}
280
281} // namespace MayaFlux::Nodes::Input
glm::vec2 current
PickCallback callback
Definition Picker.cpp:22
glm::ivec3 min
glm::ivec3 max
glm::vec3 value
float threshold
HookId add(NodeHook hook, const std::type_info &key, uint8_t tag=0, std::array< double, 2 > params={})
Appends a hook with no duplicate check and returns its id.
Snapshot snapshot() const
The current entries, held stable for the lifetime of the returned object.
Definition NodeUtils.cpp:43
HookId add_conditional(NodeHook hook, NodeCondition condition, const std::type_info &key, uint8_t tag=0)
Appends a hook that fires only while condition holds.
void fire(NodeContext &ctx) const
Calls every entry whose condition, if any, holds.
Definition NodeUtils.cpp:68
uint32_t device_id
Source device ID.
Definition InputNode.hpp:51
double raw_value
Unsmoothed input value.
Definition InputNode.hpp:49
Core::InputType source_type
Backend that produced this input.
Definition InputNode.hpp:50
Context for InputNode callbacks - provides input event access.
Definition InputNode.hpp:38
void update_context(double value) override
Update context after processing.
Definition InputNode.cpp:98
HookId on_tick(const TypedHook< InputContext > &callback)
Register callback for any input received.
std::optional< Core::InputValue > get_last_input() const
Get the most recent raw InputValue.
Definition InputNode.cpp:63
HookId on_button_press(const TypedHook< InputContext > &callback)
Register callback for button press (0.0 → 1.0 transition)
HookId on_range_exit(double min, double max, const TypedHook< InputContext > &callback)
Register callback for exiting a value range.
bool has_new_input()
Check if new input has arrived since last check.
Definition InputNode.cpp:58
std::vector< Core::InputValue > get_input_history() const
Get input history (thread-safe copy)
Definition InputNode.cpp:71
HookId on_tick_if(const NodeCondition &condition, const TypedHook< InputContext > &callback)
Register conditional callback for input received.
NodeContext & get_last_context() override
Retrieves the last created context object.
HookId add_input_callback(const TypedHook< InputContext > &callback, InputEventType event_type, std::optional< double > threshold={}, std::optional< std::pair< double, double > > range={}, std::optional< NodeCondition > condition={})
std::atomic< double > m_target_value
Memory::LockFreeQueue< Core::InputValue, 64 > m_input_history
InputNode(InputConfig config={})
Definition InputNode.cpp:9
double process_sample(double input=0.0) override
Process a single sample.
Definition InputNode.cpp:18
std::atomic< uint32_t > m_last_device_id
virtual void process_input(const Core::InputValue &value)
Process an input value from a backend.
Definition InputNode.cpp:39
std::vector< double > process_batch(unsigned int num_samples) override
Process a batch of samples.
Definition InputNode.cpp:30
void notify_tick(double value) override
Fires the hooks for one delivered input.
HookId on_threshold_rising(double threshold, const TypedHook< InputContext > &callback)
Register callback for threshold crossing (rising edge)
virtual double extract_value(const Core::InputValue &value)
Extract a scalar value from an InputValue.
Definition InputNode.cpp:76
HookId on_button_release(const TypedHook< InputContext > &callback)
Register callback for button release (1.0 → 0.0 transition)
double apply_smoothing(double target, double current) const
HookId while_in_range(double min, double max, const TypedHook< InputContext > &callback)
Register callback while value is in range.
HookId on_value_change(const TypedHook< InputContext > &callback, double epsilon=0.0001)
Register callback for value changes.
std::atomic< bool > m_has_new_input
HookId on_threshold_falling(double threshold, const TypedHook< InputContext > &callback)
Register callback for threshold crossing (falling edge)
HookId on_range_enter(double min, double max, const TypedHook< InputContext > &callback)
Register callback for entering a value range.
std::atomic< double > m_current_value
double value
Current sample value.
Definition Node.hpp:63
Base context class for node callbacks.
Definition Node.hpp:53
HookList m_conditional_callbacks
Hooks fired on a tick only while their condition holds.
Definition Node.hpp:485
double m_last_output
The most recent sample value generated by this oscillator.
Definition Node.hpp:441
HookList m_callbacks
Hooks fired on every tick.
Definition Node.hpp:478
InputEventType
Types of input events that can trigger callbacks.
Definition InputNode.hpp:14
@ BUTTON_PRESS
Button went from 0.0 to 1.0.
@ THRESHOLD_FALLING
Value crossed threshold downward.
@ VALUE_CHANGE
Value changed from previous.
@ THRESHOLD_RISING
Value crossed threshold upward.
@ CONDITIONAL
User-provided condition.
@ RANGE_ENTER
Value entered specified range.
@ RANGE_EXIT
Value exited specified range.
@ BUTTON_RELEASE
Button went from 1.0 to 0.0.
@ LINEAR
Linear interpolation between values.
@ NONE
No smoothing - immediate value changes (buttons)
@ EXPONENTIAL
Exponential smoothing / 1-pole lowpass (default)
uint64_t HookId
Identifies one registration inside a HookList.
Definition NodeUtils.hpp:60
std::function< void(ContextT &)> TypedHook
Callback function type for node processing events, parameterised on context type.
Definition NodeUtils.hpp:28
std::function< bool(NodeContext &)> NodeCondition
Predicate function type for conditional callbacks.
Definition NodeUtils.hpp:54
NodeHook adapt_hook(const TypedHook< ContextT > &hook)
Wraps a context-typed hook into a NodeHook.
@ MIDI
Structured MIDI message.
@ VECTOR
Multiple float values (e.g., accelerometer xyz)
@ SCALAR
Single normalized float [-1.0, 1.0] or [0.0, 1.0].
Generic input value container.
double slew_rate
Max change per sample (SLEW mode)
Definition InputNode.hpp:70
double smoothing_factor
0-1, higher = faster response
Definition InputNode.hpp:69
double default_value
Initial output value.
Definition InputNode.hpp:71
Configuration for InputNode behavior.
Definition InputNode.hpp:67