MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
InputNode.hpp
Go to the documentation of this file.
1#pragma once
2
5
7
9
10/**
11 * @enum InputEventType
12 * @brief Types of input events that can trigger callbacks
13 */
14enum class InputEventType : uint8_t {
15 TICK, ///< Every input received
16 VALUE_CHANGE, ///< Value changed from previous
17 THRESHOLD_RISING, ///< Value crossed threshold upward
18 THRESHOLD_FALLING, ///< Value crossed threshold downward
19 RANGE_ENTER, ///< Value entered specified range
20 RANGE_EXIT, ///< Value exited specified range
21 BUTTON_PRESS, ///< Button went from 0.0 to 1.0
22 BUTTON_RELEASE, ///< Button went from 1.0 to 0.0
23 CONDITIONAL ///< User-provided condition
24};
25
26/**
27 * @struct InputCallback
28 * @brief Callback registration with event type and optional parameters
29 */
33 std::optional<NodeCondition> condition; // For CONDITIONAL
34 std::optional<double> threshold; // For THRESHOLD_*
35 std::optional<std::pair<double, double>> range; // For RANGE_*
36};
37
38// ─────────────────────────────────────────────────────────────────────────────
39// InputContext - Specialized context for input node callbacks
40// ─────────────────────────────────────────────────────────────────────────────
41
42/**
43 * @class InputContext
44 * @brief Context for InputNode callbacks - provides input event access
45 *
46 * Similar to GeneratorContext or TextureContext, provides node-specific
47 * information to callbacks. Contains both the smoothed output value and
48 * access to the raw input data.
49 */
50class MAYAFLUX_API InputContext : public NodeContext {
51public:
52 InputContext(double value, double raw_value,
53 Core::InputType source, uint32_t device_id)
54 : NodeContext(value, typeid(InputContext).name())
55 , raw_value(raw_value)
56 , source_type(source)
57 , device_id(device_id)
58 {
59 }
60
61 double raw_value; ///< Unsmoothed input value
62 Core::InputType source_type; ///< Backend that produced this input
63 uint32_t device_id; ///< Source device ID
64};
65
66/**
67 * @brief Smoothing mode for input values
68 */
69enum class SmoothingMode : uint8_t {
70 NONE, ///< No smoothing - immediate value changes (buttons)
71 LINEAR, ///< Linear interpolation between values
72 EXPONENTIAL, ///< Exponential smoothing / 1-pole lowpass (default)
73 SLEW ///< Slew rate limiting
74};
75
76/**
77 * @brief Configuration for InputNode behavior
78 */
81 double smoothing_factor { 0.1 }; ///< 0-1, higher = faster response
82 double slew_rate { 1.0 }; ///< Max change per sample (SLEW mode)
83 double default_value { 0.0 }; ///< Initial output value
84 size_t history_size { 8 }; ///< Input history buffer size
85};
86
87// ─────────────────────────────────────────────────────────────────────────────
88// InputNode - Base class for input-driven nodes
89// ─────────────────────────────────────────────────────────────────────────────
90
91/**
92 * @class InputNode
93 * @brief Abstract base class for nodes that receive external input
94 *
95 * InputNode follows a similar pattern to GpuSync: it bridges async external
96 * data (input events) with the synchronous node processing system.
97 *
98 * Key characteristics:
99 * - `on_input()` receives async data from InputSubsystem (like compute_frame produces data)
100 * - `process_sample()` returns smoothed values for downstream consumption
101 * - `needs_processing()` indicates if new input has arrived (like needs_gpu_update)
102 * - Minimal callback overhead (input IS the event)
103 *
104 * Unlike audio Generators that produce continuous streams, InputNodes:
105 * - Are event-driven (data arrives sporadically)
106 * - Apply smoothing to convert discrete events to continuous signals
107 * - Don't drive timing (they respond to external timing)
108 *
109 * Derived classes implement:
110 * - `extract_value()`: Convert InputValue to scalar
111 * - Optionally override `on_input()` for specialized handling
112 */
113class MAYAFLUX_API InputNode : public Node {
114public:
115 // explicit InputNode(Config config);
116
117 explicit InputNode(InputConfig config = {});
118 ~InputNode() override = default;
119
120 // ─────────────────────────────────────────────────────────────────────
121 // Node Interface
122 // ─────────────────────────────────────────────────────────────────────
123
124 /**
125 * @brief Process a single sample
126 * @param input Unused (InputNodes generate from external input)
127 * @return Current smoothed input value
128 */
129 double process_sample(double input = 0.0) override;
130
131 /**
132 * @brief Process a batch of samples
133 * @param num_samples Number of samples to generate
134 * @return Vector of smoothed values
135 */
136 std::vector<double> process_batch(unsigned int num_samples) override;
137
138 // ─────────────────────────────────────────────────────────────────────
139 // Input Reception (called by InputManager)
140 // ─────────────────────────────────────────────────────────────────────
141
142 /**
143 * @brief Process an input value from a backend
144 * @param value The input value
145 *
146 * Called by InputManager when input arrives. This is the main entry point.
147 * - Extracts scalar value via extract_value()
148 * - Applies smoothing
149 * - Stores result
150 * - Fires notify_tick() which triggers user callbacks
151 *
152 * Thread-safe. Called from InputManager's processing thread.
153 */
154 virtual void process_input(const Core::InputValue& value);
155
156 /**
157 * @brief Check if new input has arrived since last check
158 * @return true if process_input() was called
159 *
160 * Useful for polling-style checks. Calling this clears the flag.
161 */
162 bool has_new_input();
163
164 /**
165 * @brief Clear the new input flag without checking
166 */
167 void clear_input_flag() { m_has_new_input.store(false); }
168
169 // ─────────────────────────────────────────────────────────────────────
170 // State Access
171 // ─────────────────────────────────────────────────────────────────────
172
173 /** @brief Get the target value (before smoothing) */
174 [[nodiscard]] double get_target_value() const { return m_target_value.load(); }
175
176 /** @brief Get the current smoothed value */
177 [[nodiscard]] double get_current_value() const { return m_current_value.load(); }
178
179 /** @brief Get the most recent raw InputValue */
180 [[nodiscard]] std::optional<Core::InputValue> get_last_input() const;
181
182 /** @brief Get input history (thread-safe copy) */
183 [[nodiscard]] std::vector<Core::InputValue> get_input_history() const;
184
185 // ─────────────────────────────────────────────────────────────────────
186 // Configuration
187 // ─────────────────────────────────────────────────────────────────────
188
189 void set_smoothing(SmoothingMode mode, double factor = 0.1)
190 {
191 m_config.smoothing = mode;
192 m_config.smoothing_factor = factor;
193 }
194 void set_slew_rate(double rate) { m_config.slew_rate = rate; }
195 [[nodiscard]] const InputConfig& get_config() const { return m_config; }
196
197 // ─────────────────────────────────────────────────────────────────────
198 // Context Access (for callbacks)
199 // ─────────────────────────────────────────────────────────────────────
200
201 NodeContext& get_last_context() override { return m_context; }
202
203 /**
204 * @brief Register callback for any input received
205 * @param callback Function to call on every input
206 *
207 * Alias for on_tick() - fires on every process_input() call
208 */
209 void on_input(const NodeHook& callback)
210 {
211 on_tick(callback);
212 }
213
214 /**
215 * @brief Register callback for value changes
216 * @param callback Function to call when value differs from previous
217 * @param epsilon Minimum change to trigger (default: 0.0001)
218 */
219 void on_value_change(const NodeHook& callback, double epsilon = 0.0001);
220
221 /**
222 * @brief Register callback for threshold crossing (rising edge)
223 * @param threshold Value threshold
224 * @param callback Function to call when value crosses threshold upward
225 */
226 void on_threshold_rising(double threshold, const NodeHook& callback);
227
228 /**
229 * @brief Register callback for threshold crossing (falling edge)
230 * @param threshold Value threshold
231 * @param callback Function to call when value crosses threshold downward
232 */
233 void on_threshold_falling(double threshold, const NodeHook& callback);
234
235 /**
236 * @brief Register callback for entering a value range
237 * @param min Range minimum
238 * @param max Range maximum
239 * @param callback Function to call when value enters range
240 */
241 void on_range_enter(double min, double max, const NodeHook& callback);
242
243 /**
244 * @brief Register callback for exiting a value range
245 * @param min Range minimum
246 * @param max Range maximum
247 * @param callback Function to call when value exits range
248 */
249 void on_range_exit(double min, double max, const NodeHook& callback);
250
251 /**
252 * @brief Register callback for button press (0.0 → 1.0 transition)
253 * @param callback Function to call on button press
254 *
255 * Specialized for button inputs - fires once on press
256 */
257 void on_button_press(const NodeHook& callback);
258
259 /**
260 * @brief Register callback for button release (1.0 → 0.0 transition)
261 * @param callback Function to call on button release
262 *
263 * Specialized for button inputs - fires once on release
264 */
265 void on_button_release(const NodeHook& callback);
266
267 /**
268 * @brief Register callback while value is in range
269 * @param min Range minimum
270 * @param max Range maximum
271 * @param callback Function to call continuously while in range
272 */
273 void while_in_range(double min, double max, const NodeHook& callback);
274
275protected:
276 /**
277 * @brief Extract a scalar value from an InputValue
278 * @param value The input value to extract from
279 * @return Scalar value for smoothing/output
280 *
281 * Override in derived classes for specific input types.
282 * Default handles SCALAR and MIDI CC values.
283 */
284 virtual double extract_value(const Core::InputValue& value);
285
286 /**
287 * @brief Update context after processing
288 */
289 void update_context(double value) override;
290
291 /**
292 * @brief Notify callbacks (minimal for InputNode)
293 *
294 * InputNodes fire callbacks sparingly - the input event IS the notification.
295 * Override if you need per-sample callbacks (rare for input).
296 */
297 void notify_tick(double value) override;
298
301
302private:
303 std::atomic<double> m_target_value { 0.0 };
304 std::atomic<double> m_current_value { 0.0 };
305 std::atomic<bool> m_has_new_input { false };
306 double m_previous_value {};
307 bool m_was_in_range {};
308
309 std::atomic<uint32_t> m_last_device_id { 0 };
310 Core::InputType m_last_source_type { Core::InputType::HID };
311 std::vector<InputCallback> m_input_callbacks;
312
314
315 [[nodiscard]] double apply_smoothing(double target, double current) const;
316
317 void add_input_callback(
318 const NodeHook& callback,
319 InputEventType event_type,
320 std::optional<double> threshold = {},
321 std::optional<std::pair<double, double>> range = {},
322 std::optional<NodeCondition> condition = {});
323};
324
325} // namespace MayaFlux::Nodes::Input
Policy-driven unified circular buffer implementation.
uint32_t device_id
Source device ID.
Definition InputNode.hpp:63
double raw_value
Unsmoothed input value.
Definition InputNode.hpp:61
InputContext(double value, double raw_value, Core::InputType source, uint32_t device_id)
Definition InputNode.hpp:52
Core::InputType source_type
Backend that produced this input.
Definition InputNode.hpp:62
Context for InputNode callbacks - provides input event access.
Definition InputNode.hpp:50
double get_current_value() const
Get the current smoothed value.
NodeContext & get_last_context() override
Retrieves the last created context object.
const InputConfig & get_config() const
void clear_input_flag()
Clear the new input flag without checking.
void set_smoothing(SmoothingMode mode, double factor=0.1)
Memory::LockFreeQueue< Core::InputValue, 64 > m_input_history
double get_target_value() const
Get the target value (before smoothing)
void on_input(const NodeHook &callback)
Register callback for any input received.
std::vector< InputCallback > m_input_callbacks
Abstract base class for nodes that receive external input.
Base context class for node callbacks.
Definition Node.hpp:30
Base interface for all computational processing nodes.
Definition Node.hpp:109
InputType
Input backend type enumeration.
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.
SmoothingMode
Smoothing mode for input values.
Definition InputNode.hpp:69
@ 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
Generic input value container.
std::optional< double > threshold
Definition InputNode.hpp:34
std::optional< std::pair< double, double > > range
Definition InputNode.hpp:35
std::optional< NodeCondition > condition
Definition InputNode.hpp:33
Callback registration with event type and optional parameters.
Definition InputNode.hpp:30
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
size_t history_size
Input history buffer size.
Definition InputNode.hpp:84
double default_value
Initial output value.
Definition InputNode.hpp:83
Configuration for InputNode behavior.
Definition InputNode.hpp:79