MayaFlux 0.2.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 notify_tick(output);
28 return output;
29}
30
31std::vector<double> InputNode::process_batch(unsigned int num_samples)
32{
33 std::vector<double> output(num_samples);
34 for (unsigned int i = 0; i < num_samples; ++i) {
35 output[i] = process_sample(0.0);
36 }
37 return output;
38}
39
41{
42 double extracted = extract_value(value);
43
44 double current = m_current_value.load();
45 double smoothed = apply_smoothing(extracted, current);
46
47 m_target_value.store(extracted);
48 m_current_value.store(smoothed);
49 m_last_output = smoothed;
50 m_has_new_input.store(true);
51
52 m_last_device_id.store(value.device_id);
54
55 (void)m_input_history.push(value);
56
57 notify_tick(smoothed);
58}
59
61{
62 return m_has_new_input.exchange(false);
63}
64
65std::optional<Core::InputValue> InputNode::get_last_input() const
66{
67 auto history = m_input_history.snapshot();
68 if (history.empty())
69 return std::nullopt;
70 return history.back();
71}
72
73std::vector<Core::InputValue> InputNode::get_input_history() const
74{
75 return m_input_history.snapshot();
76}
77
79{
80 switch (value.type) {
82 return value.as_scalar();
83
85 const auto& vec = value.as_vector();
86 return vec.empty() ? m_config.default_value : vec[0];
87 }
88
90 // Default: treat data2 as 0-127 normalized value
91 const auto& midi = value.as_midi();
92 return static_cast<double>(midi.data2) / 127.0;
93 }
94
95 default:
97 }
98}
99
107
108double InputNode::apply_smoothing(double target, double current) const
109{
110 switch (m_config.smoothing) {
112 return target;
113
115 double diff = target - current;
116 return current + diff * m_config.smoothing_factor;
117 }
118
120 // y[n] = a * x[n] + (1-a) * y[n-1]
121 return m_config.smoothing_factor * target + (1.0 - m_config.smoothing_factor) * current;
122 }
123
124 case SmoothingMode::SLEW: {
125 double diff = target - current;
126 if (std::abs(diff) <= m_config.slew_rate) {
127 return target;
128 }
129 return current + (diff > 0 ? m_config.slew_rate : -m_config.slew_rate);
130 }
131
132 default:
133 return target;
134 }
135}
136
137void InputNode::on_value_change(const NodeHook& callback, double epsilon)
138{
140}
141
142void InputNode::on_threshold_rising(double threshold, const NodeHook& callback)
143{
145}
146
147void InputNode::on_threshold_falling(double threshold, const NodeHook& callback)
148{
150}
151
152void InputNode::on_range_enter(double min, double max, const NodeHook& callback)
153{
154 add_input_callback(callback, InputEventType::RANGE_ENTER, {}, { { min, max } });
155}
156
157void InputNode::on_range_exit(double min, double max, const NodeHook& callback)
158{
159 add_input_callback(callback, InputEventType::RANGE_EXIT, {}, { { min, max } });
160}
161
166
171
172void InputNode::while_in_range(double min, double max, const NodeHook& callback)
173{
175 [min, max](const NodeContext& ctx) {
176 return ctx.value >= min && ctx.value <= max;
177 },
178 callback);
179}
180
182 const NodeHook& callback,
183 InputEventType event_type,
184 std::optional<double> threshold,
185 std::optional<std::pair<double, double>> range,
186 std::optional<NodeCondition> condition)
187{
188 m_input_callbacks.push_back({ .callback = callback,
189 .event_type = event_type,
190 .condition = std::move(condition),
191 .threshold = threshold,
192 .range = range });
193}
194
195void InputNode::notify_tick(double value)
196{
197 update_context(value);
198 auto& ctx = get_last_context();
199
200 for (auto& callback : m_callbacks) {
201 callback(ctx);
202 }
203 for (auto& [callback, condition] : m_conditional_callbacks) {
204 if (condition(ctx)) {
205 callback(ctx);
206 }
207 }
208
209 for (auto& cb : m_input_callbacks) {
210 bool should_fire = false;
211
212 switch (cb.event_type) {
214 should_fire = true;
215 break;
216
218 double epsilon = cb.threshold.value_or(0.0001);
219 should_fire = std::abs(value - m_previous_value) > epsilon;
220 break;
221 }
222
224 should_fire = (m_previous_value < cb.threshold.value() && value >= cb.threshold.value());
225 break;
226
228 should_fire = (m_previous_value >= cb.threshold.value() && value < cb.threshold.value());
229 break;
230
232 auto [min, max] = cb.range.value();
233 bool in_range_now = (value >= min && value <= max);
234 should_fire = (!m_was_in_range && in_range_now);
235 m_was_in_range = in_range_now;
236 break;
237 }
238
240 auto [min, max] = cb.range.value();
241 bool in_range_now = (value >= min && value <= max);
242 should_fire = (m_was_in_range && !in_range_now);
243 m_was_in_range = in_range_now;
244 break;
245 }
246
248 should_fire = (m_previous_value < 0.5 && value >= 0.5);
249 break;
250
252 should_fire = (m_previous_value >= 0.5 && value < 0.5);
253 break;
254
256 should_fire = cb.condition && cb.condition.value()(ctx);
257 break;
258 }
259
260 if (should_fire) {
261 cb.callback(ctx);
262 }
263 }
264
265 m_previous_value = value;
266}
267
268} // namespace MayaFlux::Nodes::Input
uint32_t device_id
Source device ID.
Definition InputNode.hpp:63
double raw_value
Unsmoothed input value.
Definition InputNode.hpp:61
Core::InputType source_type
Backend that produced this input.
Definition InputNode.hpp:62
void update_context(double value) override
Update context after processing.
std::optional< Core::InputValue > get_last_input() const
Get the most recent raw InputValue.
Definition InputNode.cpp:65
bool has_new_input()
Check if new input has arrived since last check.
Definition InputNode.cpp:60
std::vector< Core::InputValue > get_input_history() const
Get input history (thread-safe copy)
Definition InputNode.cpp:73
NodeContext & get_last_context() override
Retrieves the last created context object.
void on_value_change(const NodeHook &callback, double epsilon=0.0001)
Register callback for value changes.
void on_threshold_rising(double threshold, const NodeHook &callback)
Register callback for threshold crossing (rising edge)
void on_range_enter(double min, double max, const NodeHook &callback)
Register callback for entering a value range.
std::atomic< double > m_target_value
Memory::LockFreeQueue< Core::InputValue, 64 > m_input_history
void add_input_callback(const NodeHook &callback, InputEventType event_type, std::optional< double > threshold={}, std::optional< std::pair< double, double > > range={}, std::optional< NodeCondition > condition={})
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
std::vector< InputCallback > m_input_callbacks
virtual void process_input(const Core::InputValue &value)
Process an input value from a backend.
Definition InputNode.cpp:40
std::vector< double > process_batch(unsigned int num_samples) override
Process a batch of samples.
Definition InputNode.cpp:31
void on_range_exit(double min, double max, const NodeHook &callback)
Register callback for exiting a value range.
void while_in_range(double min, double max, const NodeHook &callback)
Register callback while value is in range.
void notify_tick(double value) override
Notify callbacks (minimal for InputNode)
virtual double extract_value(const Core::InputValue &value)
Extract a scalar value from an InputValue.
Definition InputNode.cpp:78
void on_threshold_falling(double threshold, const NodeHook &callback)
Register callback for threshold crossing (falling edge)
double apply_smoothing(double target, double current) const
void on_button_press(const NodeHook &callback)
Register callback for button press (0.0 → 1.0 transition)
std::atomic< bool > m_has_new_input
std::atomic< double > m_current_value
void on_button_release(const NodeHook &callback)
Register callback for button release (1.0 → 0.0 transition)
double value
Current sample value.
Definition Node.hpp:40
Base context class for node callbacks.
Definition Node.hpp:30
virtual void on_tick_if(const NodeCondition &condition, const NodeHook &callback)
Registers a conditional callback.
Definition Node.cpp:10
std::vector< NodeHook > m_callbacks
Collection of standard callback functions.
Definition Node.hpp:406
double m_last_output
The most recent sample value generated by this oscillator.
Definition Node.hpp:377
std::vector< std::pair< NodeHook, NodeCondition > > m_conditional_callbacks
Collection of conditional callback functions with their predicates.
Definition Node.hpp:416
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)
std::function< void(NodeContext &)> NodeHook
Callback function type for node processing events.
Definition NodeUtils.hpp:25
uint32_t device_id
Source device identifier.
const std::vector< double > & as_vector() const
@ 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].
InputType source_type
Backend that generated this value.
const MIDIMessage & as_midi() const
Generic input value container.
double slew_rate
Max change per sample (SLEW mode)
Definition InputNode.hpp:82
double smoothing_factor
0-1, higher = faster response
Definition InputNode.hpp:81
double default_value
Initial output value.
Definition InputNode.hpp:83
Configuration for InputNode behavior.
Definition InputNode.hpp:79