fundsp 0.12.0

Audio processing and synthesis library.
Documentation
//! Make some granular noises. Please run me in release mode!
#![allow(clippy::precedence)]

use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};

use fundsp::hacker::*;

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(),
    }
}

fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
where
    T: cpal::Sample,
{
    let sample_rate = config.sample_rate.0 as f64;
    let channels = config.channels as usize;

    let c = Granular64::new(
        2,
        32,
        2.0,
        60,
        1,
        0.05,
        0.15,
        0.0,
        #[allow(unused_variables)]
        /*|t, b, v, x, y, z| {
            let f = xerp11(60.0, 3000.0, x);
            let r = max(1.0, xerp11(0.50, 2.00, z));
            let f = f * xerp11(1.0 / r, r, sin_hz(8.0, t));
            (
                0.06,
                0.03,
                Box::new(
                    soft_saw_hz(f) * xerp11(0.005, 0.05, y) >> pan(v * 0.6),
                ),
            )
        },*/
        /*|t, b, v, x, y, z| {
            let scale = [
                44.0, 46.0, 49.0, 51.0, 53.0, 55.0, 56.0, 58.0, 61.0, 63.0, 65.0, 67.0, 68.0, 70.0,
                73.0, 75.0, 77.0, 79.0, 80.0, 82.0, 84.0, 87.0, 89.0, 91.0, 92.0,
            ];
            let d = lerp11(0.0, scale.len() as f64 - 0.01, x);
            let f = midi_hz(scale[d as usize] + 0.02 * (d - round(d)));
            (
                0.06,
                0.025,
                Box::new(
                    saw_hz(f) * 0.05
                        >> moog_hz(xerp11(20.0, 20000.0, y), lerp11(0.1, 0.65, z))
                        >> pan(v * 0.8),
                ),
            )
        },*/
        /*|t, b, v, x, y, z| {
            let f = xerp11(60.0, 3000.0, x);
            let c = xerp11(2.0, 200.0, y);
            (
                0.10,
                0.03,
                Box::new(
                    (white() >> lowpass_hz(c, 1.0)) * sine_hz(f) * (xerp11(1.0, 10.0, z) / sqrt(c))
                        >> pan(v * 0.5),
                ),
            )
        },*/
        /*|t, b, v, x, y, z| {
            let b = 2.0 * b;
            let d = b - round(b);
            (
                0.08,
                0.04,
                Box::new(
                    (pink() | dc((xerp11(50.0, 5000.0, x - 0.1 * d), xerp11(10.0, 100.0, (x - d * 2.0) * 0.5))))
                        >> !resonator() >> resonator() * 0.005
                        >> pan(v * (d + 0.5)),
                ),
            )
        },*/
        /*|t, b, v, x, y, z| {
            let scale = [
                44.0, 46.0, 49.0, 51.0, 53.0, 55.0, 56.0, 58.0, 61.0, 63.0, 65.0, 67.0, 68.0, 70.0,
                73.0, 75.0, 77.0, 79.0, 80.0, 82.0, 84.0, 87.0, 89.0, 91.0, 92.0,
            ];
            let d = lerp11(0.0, scale.len() as f64 - 0.01, x);
            let f = midi_hz(scale[d as usize] + 0.02 * (d - round(d)));
            //let f = xerp11(60.0, 3000.0, x);
            let d = 0.5 + xerp11(0.1, 10.0, y);
            (
                0.050,
                0.025,
                Box::new(
                    sine_hz(f)
                        >> shape(Shape::Tanh(d)) * (xerp11(0.002, 0.02, z) / max(0.2, a_weight(f)))
                        >> pan(v * 0.7),
                ),
            )
        },*/
        /*|t, b, v, x, y, z| {
            let scale = [
                36.0, 38.0, 41.0, 43.0, 46.0, 48.0, 50.0, 53.0, 55.0, 58.0, 60.0, 62.0, 65.0, 67.0,
                70.0, 72.0, 74.0, 77.0, 79.0, 82.0, 84.0,
            ];
            let d = lerp11(0.0, scale.len() as f64 - 0.01, x);
            let f = midi_hz(scale[d as usize] + 0.02 * (d - round(d)));
            (
                0.070,
                0.035,
                Box::new(
                    dc((f, lerp11(0.50, 0.99, y)))
                    >> pulse() >> peak_hz(xerp11(60.0, 10000.0, z), 5.0) * (0.01 / a_weight(f))
                    >> pan(v * 0.7),
                ),
            )
        },*/
        |t, b, v, x, y, z| {
            let scale = [
                36.0, 38.0, 40.0, 43.0, 45.0, 48.0, 50.0, 52.0, 55.0, 57.0, 60.0, 62.0, 64.0, 67.0,
                69.0, 72.0, 74.0, 76.0, 79.0, 81.0, 84.0, 86.0, 88.0, 91.0, 93.0, 96.0,
            ];
            let d = lerp11(0.0, scale.len() as f64 - 0.01, x);
            let f = midi_hz(scale[d as usize] + 0.02 * (d - round(d)));
            (
                0.06,
                0.02,
                Box::new(
                    organ_hz(f) * (0.05 / a_weight(f))
                        >> moog_hz(xerp11(20.0, 20000.0, y), lerp11(0.10, 0.65, z))
                        >> pan(v * 0.7),
                ),
            )
        },
    );

    let mut c = Net64::wrap(Box::new(c));
    c = c >> (multipass() & 0.1 * reverb_stereo(20.0, 2.0)) >> (dcblock() | dcblock());

    c.reset(Some(sample_rate));

    let mut c = BlockRateAdapter64::new(Box::new(c));

    let mut next_value = move || c.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,
    )?;
    stream.play()?;

    std::thread::sleep(std::time::Duration::from_millis(120_000));

    Ok(())
}

fn write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> (f64, f64))
where
    T: cpal::Sample,
{
    for frame in output.chunks_mut(channels) {
        let sample = next_sample();
        let left: T = cpal::Sample::from::<f32>(&(sample.0 as f32));
        let right: T = cpal::Sample::from::<f32>(&(sample.1 as f32));

        for (channel, sample) in frame.iter_mut().enumerate() {
            if channel & 1 == 0 {
                *sample = left;
            } else {
                *sample = right;
            }
        }
    }
}