scsynth 0.1.0

A safe Rust wrapper for an embedded, in-process SuperCollider scsynth engine.
//! Proves buffer read-back: values written into an engine soundbuffer round-trip back through the
//! safe [`World::copy_buffer`] API.
//!
//! This is the native verification of the `World_CopySndBuf` wrapper that also serves the wasm build
//! (where the buffer commands run inline rather than on the NRT helper thread).

mod common;

use scsynth::{Error, Options, World};

const SAMPLE_RATE: u32 = 48_000;
const BLOCK: usize = 64;
const CHANNELS: usize = 1;

#[test]
fn copies_buffer_contents() {
    let mut world = World::new(
        Options::new()
            .real_time(true)
            .input_channels(0)
            .output_channels(CHANNELS as u32)
            .sample_rate(SAMPLE_RATE)
            .block_size(BLOCK as u32)
            .load_synthdefs(false)
            .verbosity(-1),
    )
    .expect("World_New failed");

    // Allocate an 8-frame mono buffer; `/b_alloc` is sequenced, so wait for its `/done`.
    world.send_packet(&common::encode(common::b_alloc(0, 8, 1)));
    common::pump_for_reply(&mut world, "/done", CHANNELS, 200);

    // Write known values, then pump so the (RT-context) `/b_setn` is performed before we read back.
    let values = [0.0, 0.25, -0.5, 0.75, -1.0, 0.125, -0.375, 1.0];
    world.send_packet(&common::encode(common::b_setn(0, 0, &values)));
    let mut scratch = vec![0f32; BLOCK * CHANNELS];
    for _ in 0..10 {
        world.process(&[], 0, &mut scratch, CHANNELS);
    }

    let buf = world.copy_buffer(0).expect("copy_buffer(0) failed");
    assert_eq!(buf.channels, 1, "expected a mono buffer");
    assert_eq!(buf.frames, 8, "expected 8 frames");
    assert_eq!(buf.data, values, "buffer contents did not round-trip");

    // An out-of-range index is reported as an error rather than read.
    assert!(
        matches!(
            world.copy_buffer(99_999),
            Err(Error::BufferIndexOutOfRange(99_999))
        ),
        "expected an out-of-range error for a bogus buffer index"
    );
}