vst-audio-ring 0.7.0

Bounded atomic shared-memory audio block ring for isolated plugin workers
Documentation
#![forbid(unsafe_code)]

use std::sync::atomic::{AtomicU32, Ordering};
use std::time::Duration;

use assert_no_alloc::{AllocDisabler, assert_no_alloc};
use vst_audio_ring::{
    BlockLane, Event, EventBuffer, Geometry, Producer, Publish, RegionBuf, SampleFormat, Signal,
    Wait, Waited,
};

#[global_allocator]
static ALLOCATOR: AllocDisabler = AllocDisabler;

#[derive(Clone, Copy)]
struct Noop;

impl Signal for Noop {
    fn signal(&self) {}
}

impl Wait for Noop {
    fn wait(&self, _timeout: Duration) -> Waited {
        Waited::TimedOut
    }
}

#[test]
fn producer_per_block_path_does_not_allocate() {
    let storage = RegionBuf::new(Geometry {
        sample_format: vst_audio_ring::SampleFormat::F32,
        lanes: 1,
        slots: 2,
        input_channels: 2,
        output_channels: 6,
        max_frames: 480,
        sample_rate: 48_000,
        latency_bound_frames: 1_024,
    })
    .unwrap();
    let noop = Noop;
    let mut producer = Producer::new(storage.region(), [Some(&noop), None], &noop);
    producer
        .announce(
            Geometry {
                sample_format: vst_audio_ring::SampleFormat::F32,
                lanes: 1,
                slots: 2,
                input_channels: 2,
                output_channels: 6,
                max_frames: 480,
                sample_rate: 48_000,
                latency_bound_frames: 1_024,
            },
            1,
        )
        .unwrap();
    let mut consumer = vst_audio_ring::Consumer::new(storage.region(), &noop, [None, None]);
    consumer.validate_and_ready(2).unwrap();
    let publisher = producer.publisher().unwrap();
    let touched = AtomicU32::new(0);
    let output_event = Event::midi(17, 0, [0x90, 60, 100]);
    assert_eq!(
        publisher.try_publish(storage.region(), 0, 1, Some(&noop), |slot| {
            slot.write(0, 0, 0.5);
        }),
        Publish::Published
    );
    let mut events = EventBuffer::default();
    events.push(output_event);
    assert!(consumer.output_packet(0, 1, &events));
    consumer.complete(0, 1);
    let mut output = EventBuffer::default();

    assert_no_alloc(|| {
        assert_eq!(publisher.completed(storage.region(), 0), 1);
        assert!(publisher.try_read(storage.region(), 0, 1, |slot| {
            assert_eq!(slot.read(0, 0), 0.5);
            assert!(slot.output_events(&mut output));
            assert_eq!(output.as_slice(), [output_event]);
            touched.fetch_add(1, Ordering::Relaxed);
        }));
    });
    assert_eq!(touched.load(Ordering::Relaxed), 1);
}

#[test]
fn block_lane_planes_do_not_allocate() {
    for sample_format in [SampleFormat::F32, SampleFormat::F64] {
        let geometry = Geometry {
            sample_format,
            lanes: 1,
            slots: 2,
            input_channels: 1,
            output_channels: 1,
            max_frames: 2,
            sample_rate: 48_000,
            latency_bound_frames: 1_024,
        };
        let storage = std::sync::Arc::new(RegionBuf::new(geometry).unwrap());
        let noop = Noop;
        let mut producer = Producer::new(storage.region(), [Some(&noop), None], &noop);
        producer.announce(geometry, 1).unwrap();
        let publisher = producer.publisher().unwrap();
        let mut consumer = vst_audio_ring::Consumer::new(storage.region(), &noop, [None, None]);
        consumer.validate_and_ready(2).unwrap();
        let mut lane = BlockLane::new(storage.clone(), noop, publisher, 0, 1).unwrap();

        match sample_format {
            SampleFormat::F32 => {
                let input = [0.25, 0.5];
                let mut output = [0.0; 2];
                assert!(lane.process_planes(&input, &mut output).unwrap().published);
                let sequence = consumer.next_published(0).unwrap();
                let mut samples = [0.0; 2];
                assert!(consumer.slot_mut(0, sequence, &mut samples, |samples| {
                    samples.copy_from_slice(&[0.75, 1.0]);
                }));
                consumer.complete(0, sequence);
                assert_no_alloc(|| {
                    let exchange = lane.process_planes(&input, &mut output).unwrap();
                    assert!(exchange.delivered);
                    assert_eq!(output, [0.75, 1.0]);
                });
            }
            SampleFormat::F64 => {
                let input = [0.25, 0.5];
                let mut output = [0.0; 2];
                assert!(
                    lane.process_planes_f64(&input, &mut output)
                        .unwrap()
                        .published
                );
                let sequence = consumer.next_published(0).unwrap();
                let mut samples = [0.0; 2];
                assert!(consumer.slot_mut_f64(0, sequence, &mut samples, |samples| {
                    samples.copy_from_slice(&[0.75, 1.0]);
                }));
                consumer.complete(0, sequence);
                assert_no_alloc(|| {
                    let exchange = lane.process_planes_f64(&input, &mut output).unwrap();
                    assert!(exchange.delivered);
                    assert_eq!(output, [0.75, 1.0]);
                });
            }
        }
    }
}