1pub use smallvec;
2
3pub mod scratch;
4pub mod vec;
5
6pub use scratch::{Scratch, ScratchPool};
7pub use vec::{BiquadState, EnvelopeMode, EnvelopeState, FilterType, Waveform};
8
9#[macro_export]
34macro_rules! sesh_plugin {
35 ($fn_name:ident) => {
36 #[no_mangle]
37 pub extern "C" fn sesh_sdk_version() -> u32 {
38 2
39 }
40
41 #[no_mangle]
42 pub extern "C" fn sesh_alloc(size: usize, align: usize) -> *mut u8 {
43 let layout = ::std::alloc::Layout::from_size_align(size, align).expect("invalid layout");
44 unsafe { ::std::alloc::alloc(layout) }
45 }
46
47 #[no_mangle]
48 pub extern "C" fn sesh_free(ptr: *mut u8, size: usize, align: usize) {
49 let layout = ::std::alloc::Layout::from_size_align(size, align).expect("invalid layout");
50 unsafe { ::std::alloc::dealloc(ptr, layout) }
51 }
52
53 #[no_mangle]
54 pub extern "C" fn sesh_process(ptr: *mut f32, frames: usize, channels: usize) {
55 let buffer = unsafe { ::std::slice::from_raw_parts_mut(ptr, frames * channels) };
56 let mut chunks: $crate::smallvec::SmallVec<[&mut [f32]; 32]> =
57 buffer.chunks_mut(frames).collect();
58 $fn_name(&mut chunks);
59 }
60
61 #[cfg(test)]
62 mod sesh_auto_tests {
63 #[test]
64 fn impulse_response_safety() {
65 const SR: usize = 44100;
66 const DURATION: usize = SR * 10;
67 const CHUNK: usize = 128;
68
69 let mut left = vec![0.0f32; DURATION];
70 let mut right = vec![0.0f32; DURATION];
71 left[0] = 1.0;
72 right[0] = 1.0;
73
74 for start in (0..DURATION).step_by(CHUNK) {
75 let end = (start + CHUNK).min(DURATION);
76 let mut channels: [&mut [f32]; 2] = [
77 &mut left[start..end],
78 &mut right[start..end],
79 ];
80 super::$fn_name(&mut channels);
81 }
82
83 let peak = left.iter().chain(right.iter())
84 .fold(0.0f32, |m, s| m.max(s.abs()));
85
86 assert!(peak < 10.0,
87 "impulse response peak {:.1} exceeds 10.0 — \
88 likely runaway feedback (check feedback gain, \
89 input injection levels, and mixing matrix energy)",
90 peak);
91 }
92 }
93 };
94}