I appended a full example. The network plays a sine wave at 440 Hz. After replacing signal with the same sine wave, the pitch is higher. I don't think the need to set the sample rate after a replace is intended behavior.
On a side note, I understand that declick fades in the signal. When I replace the signal node, a click happens even when the pitch is the same. Is it possible to do a fade in on every replace? Thanks a lot in advance.
//! Make real-time changes to a network while it is playing.
#![allow(clippy::precedence)]
use assert_no_alloc::*;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::{FromSample, SizedSample};
use fundsp::hacker::*;
use std::time::Duration;
#[cfg(debug_assertions)] // required when disable_release is set (default)
#[global_allocator]
static A: AllocDisabler = AllocDisabler;
fn main() {
let host = cpal::default_host();
let device = host
.default_output_device()
.expect("Failed to find a default output device");
let config = device.default_output_config().unwrap();
match config.sample_format() {
cpal::SampleFormat::F32 => run::<f32>(&device, &config.into()).unwrap(),
cpal::SampleFormat::I16 => run::<i16>(&device, &config.into()).unwrap(),
cpal::SampleFormat::U16 => run::<u16>(&device, &config.into()).unwrap(),
_ => panic!("Unsupported format"),
}
}
fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
where
T: SizedSample + FromSample<f64>,
{
let sample_rate = config.sample_rate.0 as f64;
let channels = config.channels as usize;
// prints 48000
println!("{sample_rate}");
let a = Box::new(sine_hz(440.0));
let mut net = Net64::new(0, 2);
let signal = net.chain(a.clone());
net.chain(Box::new(declick() >> pan(0.0)));
net.set_sample_rate(sample_rate);
let mut backend = net.backend();
let mut next_value = move || assert_no_alloc(|| backend.get_stereo());
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
let stream = device.build_output_stream(
config,
move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
write_data(data, channels, &mut next_value)
},
err_fn,
None,
)?;
stream.play()?;
std::thread::sleep(Duration::from_secs(3));
println!("replace signal with the same function, but now it outputs a higher pitch");
net.replace(signal, a.clone());
// uncomment to set sample rate again and the pitch stays at 440 Hz
// net.set_sample_rate(sample_rate);
net.commit();
std::thread::sleep(Duration::from_secs(3));
Ok(())
}
fn write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> (f64, f64))
where
T: SizedSample + FromSample<f64>,
{
for frame in output.chunks_mut(channels) {
let sample = next_sample();
let left = T::from_sample(sample.0);
let right: T = T::from_sample(sample.1);
for (channel, sample) in frame.iter_mut().enumerate() {
if channel & 1 == 0 {
*sample = left;
} else {
*sample = right;
}
}
}
}
I appended a full example. The network plays a sine wave at 440 Hz. After replacing
signal
with the same sine wave, the pitch is higher. I don't think the need to set the sample rate after areplace
is intended behavior.On a side note, I understand that
declick
fades in the signal. When I replace the signal node, a click happens even when the pitch is the same. Is it possible to do a fade in on everyreplace
? Thanks a lot in advance.