use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use rill_core::math::Transcendental;
use rill_core::prelude::{
Node, NodeCategory, NodeId, NodeMetadata, NodeState, ParamValue, ParameterId, Port,
ProcessResult,
};
use rill_core::queues::spsc::SpscQueue;
use rill_core::queues::TelemetryBlock;
use rill_core::time::ClockTick;
use rill_core::traits::Processor;
pub struct TelemetryProbe<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize> {
id: NodeId,
inputs: Vec<Port<T, BUF_SIZE>>,
outputs: Vec<Port<T, BUF_SIZE>>,
state: NodeState<T, BUF_SIZE>,
queue: Arc<SpscQueue<TelemetryBlock<T, BUF_SIZE>, QUEUE_CAP>>,
interval: u32,
counter: u32,
block_index: u64,
channel: u32,
node_name: String,
}
impl<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize>
TelemetryProbe<T, BUF_SIZE, QUEUE_CAP>
{
pub fn new(
queue: Arc<SpscQueue<TelemetryBlock<T, BUF_SIZE>, QUEUE_CAP>>,
interval: u32,
channel: u32,
node_name: &str,
) -> Self {
assert!(interval > 0, "interval must be positive");
let id = NodeId(0);
let inputs = vec![Port::input(id, 0, "signal_in")];
let outputs = vec![Port::output(id, 0, "signal_out")];
Self {
id,
inputs,
outputs,
state: NodeState::new(44100.0),
queue,
interval,
counter: 0,
block_index: 0,
channel,
node_name: node_name.to_string(),
}
}
pub fn queue(&self) -> &Arc<SpscQueue<TelemetryBlock<T, BUF_SIZE>, QUEUE_CAP>> {
&self.queue
}
}
impl<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize> Node<T, BUF_SIZE>
for TelemetryProbe<T, BUF_SIZE, QUEUE_CAP>
{
fn metadata(&self) -> NodeMetadata {
let mut meta = NodeMetadata::new(&self.node_name, NodeCategory::Analyzer);
meta.description = "Pass-through telemetry probe".to_string();
meta.author = "Rill".to_string();
meta.version = env!("CARGO_PKG_VERSION").to_string();
meta.signal_inputs = self.inputs.len();
meta.signal_outputs = self.outputs.len();
meta
}
fn init(&mut self, sample_rate: f32) {
self.state = NodeState::new(sample_rate);
}
fn reset(&mut self) {
self.state.reset();
self.counter = 0;
self.block_index = 0;
}
fn get_parameter(&self, _id: &ParameterId) -> Option<ParamValue> {
None
}
fn set_parameter(&mut self, _id: &ParameterId, _value: ParamValue) -> ProcessResult<()> {
Ok(())
}
fn id(&self) -> NodeId {
self.id
}
fn set_id(&mut self, id: NodeId) {
self.id = id;
}
fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.inputs.get(index)
}
fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.inputs.get_mut(index)
}
fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
self.outputs.get(index)
}
fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
self.outputs.get_mut(index)
}
fn control_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> {
None
}
fn control_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
None
}
fn num_signal_inputs(&self) -> usize {
self.inputs.len()
}
fn num_signal_outputs(&self) -> usize {
self.outputs.len()
}
fn num_control_inputs(&self) -> usize {
0
}
fn num_control_outputs(&self) -> usize {
0
}
fn state(&self) -> &NodeState<T, BUF_SIZE> {
&self.state
}
fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
&mut self.state
}
}
impl<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize> Processor<T, BUF_SIZE>
for TelemetryProbe<T, BUF_SIZE, QUEUE_CAP>
{
fn process(
&mut self,
_clock: &ClockTick,
signal_inputs: &[&[T; BUF_SIZE]],
_control_inputs: &[T],
_clock_inputs: &[ClockTick],
_feedback_inputs: &[&[T; BUF_SIZE]],
) -> ProcessResult<()> {
let silence = [T::ZERO; BUF_SIZE];
let input = signal_inputs.first().copied().unwrap_or(&silence);
if let Some(port) = self.outputs.first_mut() {
port.buffer.as_mut_array().copy_from_slice(input);
}
self.counter += 1;
if self.counter >= self.interval {
self.counter = 0;
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_micros() as u64;
let mut frame = TelemetryBlock {
node_id: self.id,
channel: self.channel,
sample_rate: self.state.sample_rate,
block_index: self.block_index,
timestamp,
..Default::default()
};
frame.data.copy_from_slice(input);
frame.compute_metrics();
self.block_index += 1;
let _ = self.queue.push(frame);
}
self.state.advance();
Ok(())
}
fn latency(&self) -> usize {
0
}
}