use crate::nodes::ProcessContext;
use crate::DspRunner;
use dirtydata_core::ir::Graph;
use dirtydata_observer::divergence::{DivergenceMap, DivergencePoint};
pub struct OfflineRenderer {
runner: DspRunner,
sample_rate: f32,
}
impl OfflineRenderer {
pub fn new(graph: Graph, sample_rate: f32) -> Self {
let runner = DspRunner::new(graph, None, sample_rate);
Self { runner, sample_rate }
}
pub fn render(&mut self, duration_secs: f32) -> Vec<f32> {
let num_samples = (duration_secs * self.sample_rate) as usize;
let mut output = Vec::with_capacity(num_samples * 2);
for i in 0..num_samples {
let ctx = ProcessContext {
sample_rate: self.sample_rate,
global_sample_index: i as u64,
crash_flag: None,
osc_tx: None,
convergence_info: None,
node_diagnostics: None,
node_id: None,
};
let sample = self.runner.process_sample(&ctx);
output.push(sample[0]);
output.push(sample[1]);
}
output
}
pub fn verify_determinism(graph: Graph, duration_secs: f32, sample_rate: f32) -> Result<bool, String> {
let mut r1 = Self::new(graph.clone(), sample_rate);
let mut r2 = Self::new(graph, sample_rate);
let out1 = r1.render(duration_secs);
let out2 = r2.render(duration_secs);
if out1.len() != out2.len() {
return Err("Output length mismatch between identical runs".into());
}
for (s1, s2) in out1.iter().zip(out2.iter()) {
if (*s1 - *s2).abs() > 0.0 {
return Ok(false);
}
}
Ok(true)
}
pub fn compare(
graph_a: Graph,
graph_b: Graph,
duration_secs: f32,
sample_rate: f32,
) -> DivergenceMap {
let mut r_a = DspRunner::new(graph_a, None, sample_rate);
let mut r_b = DspRunner::new(graph_b, None, sample_rate);
let num_samples = (duration_secs * sample_rate) as usize;
let mut map = DivergenceMap::new();
for i in 0..num_samples {
let ctx = ProcessContext {
sample_rate,
global_sample_index: i as u64,
crash_flag: None,
osc_tx: None,
convergence_info: None,
node_diagnostics: None,
node_id: None,
};
r_a.process_sample(&ctx);
r_b.process_sample(&ctx);
let ids: Vec<_> = r_a.get_graph().nodes.keys().cloned().collect();
for id in ids {
if let (Some(out_a), Some(out_b)) = (r_a.get_node_outputs(&id), r_b.get_node_outputs(&id)) {
for (p_idx, (v_a, v_b)) in out_a.iter().zip(out_b.iter()).enumerate() {
let diff_l = (v_a[0] - v_b[0]).abs();
let diff_r = (v_a[1] - v_b[1]).abs();
let mag = diff_l.max(diff_r);
if mag > 1e-7 { map.add_point(DivergencePoint {
sample_index: i as u64,
node_id: id,
node_name: "Unknown".into(), port_idx: p_idx,
expected_value: *v_a,
actual_value: *v_b,
diff_magnitude: mag,
});
if map.points.len() > 100 {
return map; }
}
}
}
}
}
map
}
}