1use std::sync::Arc;
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use rill_core::math::Transcendental;
5use rill_core::prelude::{
6 Node, NodeCategory, NodeId, NodeMetadata, NodeState, ParamValue, ParameterId, Port,
7 ProcessResult,
8};
9use rill_core::queues::spsc::SpscQueue;
10use rill_core::queues::TelemetryBlock;
11use rill_core::time::RenderContext;
12use rill_core::traits::Processor;
13
14pub struct TelemetryProbe<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize> {
28 id: NodeId,
29 inputs: Vec<Port<T, BUF_SIZE>>,
30 outputs: Vec<Port<T, BUF_SIZE>>,
31 state: NodeState<T, BUF_SIZE>,
32
33 queue: Arc<SpscQueue<TelemetryBlock<T, BUF_SIZE>, QUEUE_CAP>>,
35
36 interval: u32,
38 counter: u32,
40 block_index: u64,
42 channel: u32,
44 node_name: String,
46}
47
48impl<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize>
49 TelemetryProbe<T, BUF_SIZE, QUEUE_CAP>
50{
51 pub fn new(
59 queue: Arc<SpscQueue<TelemetryBlock<T, BUF_SIZE>, QUEUE_CAP>>,
60 interval: u32,
61 channel: u32,
62 node_name: &str,
63 ) -> Self {
64 assert!(interval > 0, "interval must be positive");
65
66 let id = NodeId(0);
67 let inputs = vec![Port::input(id, 0, "signal_in")];
68
69 let outputs = vec![Port::output(id, 0, "signal_out")];
70
71 Self {
72 id,
73 inputs,
74 outputs,
75 state: NodeState::new(44100.0),
76 queue,
77 interval,
78 counter: 0,
79 block_index: 0,
80 channel,
81 node_name: node_name.to_string(),
82 }
83 }
84
85 pub fn queue(&self) -> &Arc<SpscQueue<TelemetryBlock<T, BUF_SIZE>, QUEUE_CAP>> {
87 &self.queue
88 }
89}
90
91impl<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize> Node<T, BUF_SIZE>
94 for TelemetryProbe<T, BUF_SIZE, QUEUE_CAP>
95{
96 fn metadata(&self) -> NodeMetadata {
97 let mut meta = NodeMetadata::new(&self.node_name, NodeCategory::Analyzer);
98 meta.description = "Pass-through telemetry probe".to_string();
99 meta.author = "Rill".to_string();
100 meta.version = env!("CARGO_PKG_VERSION").to_string();
101 meta.signal_inputs = self.inputs.len();
102 meta.signal_outputs = self.outputs.len();
103 meta
104 }
105
106 fn init(&mut self, sample_rate: f32) {
107 self.state = NodeState::new(sample_rate);
108 }
109
110 fn reset(&mut self) {
111 self.state.reset();
112 self.counter = 0;
113 self.block_index = 0;
114 }
115
116 fn get_parameter(&self, _id: &ParameterId) -> Option<ParamValue> {
117 None
118 }
119
120 fn set_parameter(&mut self, _id: &ParameterId, _value: ParamValue) -> ProcessResult<()> {
121 Ok(())
122 }
123
124 fn id(&self) -> NodeId {
125 self.id
126 }
127
128 fn set_id(&mut self, id: NodeId) {
129 self.id = id;
130 }
131
132 fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
133 self.inputs.get(index)
134 }
135
136 fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
137 self.inputs.get_mut(index)
138 }
139
140 fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
141 self.outputs.get(index)
142 }
143
144 fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
145 self.outputs.get_mut(index)
146 }
147
148 fn control_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> {
149 None
150 }
151
152 fn control_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
153 None
154 }
155
156 fn num_signal_inputs(&self) -> usize {
157 self.inputs.len()
158 }
159
160 fn num_signal_outputs(&self) -> usize {
161 self.outputs.len()
162 }
163
164 fn num_control_inputs(&self) -> usize {
165 0
166 }
167
168 fn num_control_outputs(&self) -> usize {
169 0
170 }
171
172 fn state(&self) -> &NodeState<T, BUF_SIZE> {
173 &self.state
174 }
175
176 fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
177 &mut self.state
178 }
179}
180
181impl<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize> Processor<T, BUF_SIZE>
184 for TelemetryProbe<T, BUF_SIZE, QUEUE_CAP>
185{
186 fn process(
187 &mut self,
188 _ctx: &RenderContext,
189 signal_inputs: &[&[T; BUF_SIZE]],
190 _control_inputs: &[T],
191 _clock_inputs: &[RenderContext],
192 _feedback_inputs: &[&[T; BUF_SIZE]],
193 ) -> ProcessResult<()> {
194 let silence = [T::ZERO; BUF_SIZE];
196 let input = signal_inputs.first().copied().unwrap_or(&silence);
197 if let Some(port) = self.outputs.first_mut() {
198 port.write().copy_from_slice(input);
199 }
200
201 self.counter += 1;
203 if self.counter >= self.interval {
204 self.counter = 0;
205
206 let timestamp = SystemTime::now()
207 .duration_since(UNIX_EPOCH)
208 .unwrap_or_default()
209 .as_micros() as u64;
210
211 let mut frame = TelemetryBlock {
212 node_id: self.id,
213 channel: self.channel,
214 sample_rate: self.state.sample_rate,
215 block_index: self.block_index,
216 timestamp,
217 ..Default::default()
218 };
219 frame.data.copy_from_slice(input);
220 frame.compute_metrics();
221
222 self.block_index += 1;
223
224 let _ = self.queue.push(frame);
226 }
227
228 self.state.advance();
229 Ok(())
230 }
231
232 fn latency(&self) -> usize {
233 0
234 }
235}