use crate as knyst;
use knyst::{gen::GenState, Sample, SampleRate};
use knyst_macro::impl_gen;
use crate::time::Seconds;
#[inline(always)]
pub fn is_trigger(sample: Sample) -> bool {
sample > 0.
}
#[inline]
pub fn is_trigger_in_place(inputs: &[Sample], outputs: &mut [bool]) {
for (inp, out) in inputs.iter().zip(outputs.iter_mut()) {
*out = *inp > 0.
}
}
pub struct OnceTrig(bool);
#[impl_gen]
impl OnceTrig {
#[new]
#[allow(missing_docs)]
pub fn new() -> Self {
OnceTrig(false)
}
#[process]
pub fn process(&mut self, trig: &mut [Sample]) -> GenState {
let out = trig;
if self.0 {
for o in out.iter_mut() {
*o = 0.
}
} else {
out[0] = 1.0;
self.0 = true;
for o in out.iter_mut().skip(1) {
*o = 0.
}
}
GenState::FreeSelf
}
}
pub struct IntervalTrig {
counter: Seconds,
}
#[impl_gen]
impl IntervalTrig {
#[new]
#[allow(missing_docs)]
pub fn new() -> Self {
Self {
counter: Seconds::ZERO,
}
}
#[process]
fn process(
&mut self,
sample_rate: SampleRate,
interval: &[Sample],
trig: &mut [Sample],
) -> GenState {
let one_sample = Seconds::from_samples(1, *sample_rate as u64);
for (interval, trig_out) in interval.iter().zip(trig.iter_mut()) {
self.counter += one_sample;
let interval_as_seconds = Seconds::from_seconds_f64(*interval as f64);
*trig_out = if self.counter >= interval_as_seconds {
self.counter = self
.counter
.checked_sub(interval_as_seconds)
.expect("Counter was checked to be bigger than or equal to the interval so the subtraction should always work.");
1.0
} else {
0.0
};
}
GenState::Continue
}
#[init]
fn init(&mut self) {
self.counter = Seconds::ZERO;
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use crate::resources::{Resources, ResourcesSettings};
use crate::prelude::*;
use crate::time::Seconds;
use crate::trig::IntervalTrig;
use crate::*;
#[test]
fn regular_interval_trig() {
const SR: u64 = 44100;
const BLOCK_SIZE: usize = 128 as usize;
let graph_settings = GraphSettings {
block_size: BLOCK_SIZE,
sample_rate: SR as Sample,
num_outputs: 2,
..Default::default()
};
let mut graph = Graph::new(graph_settings);
let node = graph.push(IntervalTrig::new());
graph.connect(node.to_graph_out()).unwrap();
let every_8_samples = Seconds::from_samples(8, SR).to_seconds_f64();
graph
.connect(constant(every_8_samples as Sample).to(node))
.unwrap();
let (mut run_graph, _, _) = RunGraph::new(
&mut graph,
Resources::new(ResourcesSettings::default()),
RunGraphSettings {
scheduling_latency: Duration::new(0, 0),
},
)
.unwrap();
graph.update();
run_graph.process_block();
for (i, out) in run_graph
.graph_output_buffers()
.get_channel(0)
.iter()
.skip(7)
.enumerate()
{
assert_eq!(
*out,
if i % 8 == 0 { 1.0 } else { 0.0 },
"Failed at sample {i}"
);
}
}
}