cmajor 0.7.0

Rust bindings for the Cmajor JIT engine.
Documentation
use {
    cmajor::{performer::OutputStream, Cmajor},
    cpal::traits::{DeviceTrait, HostTrait, StreamTrait},
    std::{thread::sleep, time::Duration},
};

const PLAY_A_TUNE: &str = r#"
processor HelloWorld
{
    output stream float out;

    // This simple struct holds a note + duration for our melody
    struct Note
    {
        int pitch, length;

        void play() const
        {
            let numFrames = this.length * framesPerQuarterNote;
            let frequency  = std::notes::noteToFrequency (this.pitch);
            let phaseDelta = float (frequency * processor.period * twoPi);

            loop (numFrames)
            {
                out <- volume * sin (phase);
                phase = addModulo2Pi (phase, phaseDelta);
                advance();
            }
        }
    }

    // This is our processor's entry-point function, which is invoked
    // by the system
    void main()
    {
        let melody = Note[] ( (79, 1),  (77, 1),  (69, 2),  (71, 2),
                              (76, 1),  (74, 1),  (65, 2),  (67, 2),
                              (74, 1),  (72, 1),  (64, 2),  (67, 2),
                              (72, 4) );

        for (wrap<melody.size> i)
            melody[i].play();
    }

    // We'll define a couple of constants here to set the volume and tempo
    let framesPerQuarterNote = int (processor.frequency / 7);
    let volume = 0.15f;

    float phase;
}
"#;

const SAMPLE_RATE: f64 = 48_000.0;
const BLOCK_SIZE: u32 = 256;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let cmajor = Cmajor::new_from_env()?;

    println!("Cmajor v{}", cmajor.version());

    let engine = cmajor
        .create_default_engine()
        .with_sample_rate(SAMPLE_RATE)
        .build();

    let program = cmajor.parse(PLAY_A_TUNE)?;

    let mut engine = engine.load(&program)?;

    let output_stream = engine.endpoint::<OutputStream<f32>>("out")?;

    let engine = engine.link()?;

    let mut performer = engine.performer();

    performer.set_block_size(BLOCK_SIZE);

    let stream = cpal::default_host()
        .default_output_device()
        .expect("no output device")
        .build_output_stream(
            &cpal::StreamConfig {
                channels: 1,
                sample_rate: cpal::SampleRate(SAMPLE_RATE as u32),
                buffer_size: cpal::BufferSize::Fixed(BLOCK_SIZE),
            },
            move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
                performer.set_block_size(data.len() as u32);
                performer.advance();
                performer.read(output_stream, data);
            },
            |err| eprintln!("an error occurred on stream: {}", err),
            None,
        )?;

    stream.play()?;
    sleep(Duration::from_secs(5));

    Ok(())
}