use std::thread::sleep;
use std::time::{Duration, Instant};
use dasp_graph::{Buffer, Input};
use klingt::{AudioNode, CpalDevice, Klingt, ProcessContext};
use klingt::nodes::Gain;
#[derive(Clone, Copy, Debug)]
pub enum SquareMessage {
SetFrequency(f32),
SetPulseWidth(f32),
SetAmplitude(f32),
}
pub struct Square {
frequency: f32,
pulse_width: f32,
amplitude: f32,
phase: f32,
}
impl Square {
pub fn new(frequency: f32) -> Self {
Self {
frequency,
pulse_width: 0.5, amplitude: 0.25, phase: 0.0,
}
}
pub fn with_pulse_width(mut self, pw: f32) -> Self {
self.pulse_width = pw.clamp(0.0, 1.0);
self
}
}
impl AudioNode for Square {
type Message = SquareMessage;
fn process(
&mut self,
ctx: &ProcessContext,
messages: impl Iterator<Item = SquareMessage>,
_inputs: &[Input],
outputs: &mut [Buffer],
) {
for msg in messages {
match msg {
SquareMessage::SetFrequency(f) => self.frequency = f.max(0.0),
SquareMessage::SetPulseWidth(pw) => self.pulse_width = pw.clamp(0.0, 1.0),
SquareMessage::SetAmplitude(a) => self.amplitude = a.clamp(0.0, 1.0),
}
}
if outputs.is_empty() {
return;
}
let phase_inc = self.frequency / ctx.sample_rate as f32;
for sample in outputs[0].iter_mut() {
*sample = if self.phase < self.pulse_width {
self.amplitude
} else {
-self.amplitude
};
self.phase += phase_inc;
if self.phase >= 1.0 {
self.phase -= 1.0;
}
}
}
fn num_inputs(&self) -> usize { 0 }
fn num_outputs(&self) -> usize { 1 }
}
fn main() {
let device = CpalDevice::default_output().expect("No audio device found");
println!("Using: {} @ {} Hz", device.name(), device.sample_rate());
let mut klingt = Klingt::new(device.sample_rate())
.with_output(device.create_sink());
let mut square = klingt.add(Square::new(220.0).with_pulse_width(0.5));
let gain = klingt.add(Gain::new(0.5));
klingt.connect(&square, &gain);
klingt.output(&gain);
println!("Playing square wave with PWM modulation... Ctrl+C to stop\n");
let start = Instant::now();
let rate = klingt.sample_rate() as f64;
let mut blocks = 0u64;
loop {
let elapsed = start.elapsed().as_secs_f32();
let pw = 0.5 + 0.25 * (elapsed * 0.5).sin();
square.send(SquareMessage::SetPulseWidth(pw)).ok();
let freq = 220.0 + 110.0 * (elapsed * 0.2).sin();
square.send(SquareMessage::SetFrequency(freq)).ok();
let target = (start.elapsed().as_secs_f64() * rate / 64.0) as u64 + 4;
while blocks < target {
klingt.process();
blocks += 1;
}
sleep(Duration::from_millis(10));
}
}