1use std::sync::atomic::{AtomicU32, Ordering};
8
9extern "C" {
14 fn sesh_vec_version() -> u32;
15}
16
17static HOST_VEC_VERSION: AtomicU32 = AtomicU32::new(0);
19
20fn host_version() -> u32 {
21 let v = HOST_VEC_VERSION.load(Ordering::Relaxed);
22 if v != 0 {
23 return v;
24 }
25 let v = unsafe { sesh_vec_version() };
26 let store = if v == 0 { u32::MAX } else { v };
28 HOST_VEC_VERSION.store(store, Ordering::Relaxed);
29 v
30}
31
32#[inline]
33fn use_host_ops() -> bool {
34 host_version() > 0 && host_version() != u32::MAX
35}
36
37extern "C" {
42 fn sesh_vec_copy_host(dst: *mut f32, src: *const f32, len: u32);
43 fn sesh_vec_fill_host(dst: *mut f32, value: f32, len: u32);
44 fn sesh_vec_add_host(dst: *mut f32, a: *const f32, b: *const f32, len: u32);
45 fn sesh_vec_add_scalar_host(dst: *mut f32, value: f32, len: u32);
46 fn sesh_vec_mul_host(dst: *mut f32, a: *const f32, b: *const f32, len: u32);
47 fn sesh_vec_mul_scalar_host(dst: *mut f32, value: f32, len: u32);
48 fn sesh_vec_mul_add_host(dst: *mut f32, src: *const f32, gain: f32, len: u32);
49 fn sesh_vec_clamp_host(dst: *mut f32, src: *const f32, min: f32, max: f32, len: u32);
50 fn sesh_vec_ring_write_host(
51 buf: *mut f32, buf_len: u32, pos: *mut u32, src: *const f32, len: u32,
52 );
53 fn sesh_vec_ring_read_host(
54 buf: *const f32, buf_len: u32, pos: u32, dst: *mut f32, offset: u32, len: u32,
55 );
56 fn sesh_vec_delay_read_host(
57 buf: *const f32, buf_len: u32, pos: u32, dst: *mut f32, time: *const f32, len: u32,
58 );
59 fn sesh_vec_osc_host(
60 phase: *mut f32, dst: *mut f32, freq: f32, waveform: u32, sample_rate: f32, len: u32,
61 );
62 fn sesh_vec_biquad_host(
63 state: *mut f32, dst: *mut f32, src: *const f32,
64 cutoff: *const f32, q: *const f32, gain: *const f32,
65 filter_type: u32, sample_rate: f32, len: u32,
66 );
67 fn sesh_vec_envelope_host(
68 state: *mut f32, dst: *mut f32, src: *const f32,
69 attack: *const f32, release: *const f32,
70 mode: u32, sample_rate: f32, len: u32,
71 );
72 fn sesh_vec_tanh_host(dst: *mut f32, src: *const f32, drive: *const f32, len: u32);
73 fn sesh_vec_hard_clip_host(dst: *mut f32, src: *const f32, threshold: *const f32, len: u32);
74}
75
76#[repr(u32)]
82#[derive(Clone, Copy)]
83pub enum Waveform {
84 Sine = 0,
85 Triangle = 1,
86 Saw = 2,
87 Square = 3,
88}
89
90#[repr(u32)]
92#[derive(Clone, Copy)]
93pub enum FilterType {
94 Lowpass = 0,
95 Highpass = 1,
96 Bandpass = 2,
97 Notch = 3,
98 Peak = 4,
100 LowShelf = 5,
102 HighShelf = 6,
104}
105
106#[repr(C)]
108pub struct BiquadState {
109 pub x1: f32,
110 pub x2: f32,
111 pub y1: f32,
112 pub y2: f32,
113}
114
115impl BiquadState {
116 pub const fn new() -> Self {
117 Self { x1: 0.0, x2: 0.0, y1: 0.0, y2: 0.0 }
118 }
119}
120
121#[repr(u32)]
123#[derive(Clone, Copy)]
124pub enum EnvelopeMode {
125 Peak = 0,
127 Rms = 1,
129}
130
131#[repr(C)]
133pub struct EnvelopeState {
134 pub current: f32,
135}
136
137impl EnvelopeState {
138 pub const fn new() -> Self {
139 Self { current: 0.0 }
140 }
141}
142
143pub fn vec_copy(dst: &mut [f32], src: &[f32]) {
149 let len = dst.len().min(src.len());
150 if use_host_ops() {
151 unsafe { sesh_vec_copy_host(dst.as_mut_ptr(), src.as_ptr(), len as u32) }
152 } else {
153 dst[..len].copy_from_slice(&src[..len]);
154 }
155}
156
157pub fn vec_fill(dst: &mut [f32], value: f32) {
159 let len = dst.len();
160 if use_host_ops() {
161 unsafe { sesh_vec_fill_host(dst.as_mut_ptr(), value, len as u32) }
162 } else {
163 for s in dst.iter_mut() {
164 *s = value;
165 }
166 }
167}
168
169pub fn vec_add(dst: &mut [f32], a: &[f32], b: &[f32]) {
171 let len = dst.len().min(a.len()).min(b.len());
172 if use_host_ops() {
173 unsafe { sesh_vec_add_host(dst.as_mut_ptr(), a.as_ptr(), b.as_ptr(), len as u32) }
174 } else {
175 for i in 0..len {
176 dst[i] = a[i] + b[i];
177 }
178 }
179}
180
181pub fn vec_add_scalar(dst: &mut [f32], value: f32) {
183 let len = dst.len();
184 if use_host_ops() {
185 unsafe { sesh_vec_add_scalar_host(dst.as_mut_ptr(), value, len as u32) }
186 } else {
187 for s in dst.iter_mut() {
188 *s += value;
189 }
190 }
191}
192
193pub fn vec_mul(dst: &mut [f32], a: &[f32], b: &[f32]) {
195 let len = dst.len().min(a.len()).min(b.len());
196 if use_host_ops() {
197 unsafe { sesh_vec_mul_host(dst.as_mut_ptr(), a.as_ptr(), b.as_ptr(), len as u32) }
198 } else {
199 for i in 0..len {
200 dst[i] = a[i] * b[i];
201 }
202 }
203}
204
205pub fn vec_mul_scalar(dst: &mut [f32], value: f32) {
207 let len = dst.len();
208 if use_host_ops() {
209 unsafe { sesh_vec_mul_scalar_host(dst.as_mut_ptr(), value, len as u32) }
210 } else {
211 for s in dst.iter_mut() {
212 *s *= value;
213 }
214 }
215}
216
217pub fn vec_mul_add(dst: &mut [f32], src: &[f32], gain: f32) {
219 let len = dst.len().min(src.len());
220 if use_host_ops() {
221 unsafe { sesh_vec_mul_add_host(dst.as_mut_ptr(), src.as_ptr(), gain, len as u32) }
222 } else {
223 for i in 0..len {
224 dst[i] += src[i] * gain;
225 }
226 }
227}
228
229pub fn vec_clamp(dst: &mut [f32], src: &[f32], min: f32, max: f32) {
231 let len = dst.len().min(src.len());
232 if use_host_ops() {
233 unsafe { sesh_vec_clamp_host(dst.as_mut_ptr(), src.as_ptr(), min, max, len as u32) }
234 } else {
235 for i in 0..len {
236 dst[i] = src[i].clamp(min, max);
237 }
238 }
239}
240
241pub fn vec_ring_write(buf: &mut [f32], pos: &mut usize, src: &[f32]) {
248 let buf_len = buf.len();
249 let frames = src.len();
250 if use_host_ops() {
251 let mut pos32 = *pos as u32;
252 unsafe {
253 sesh_vec_ring_write_host(
254 buf.as_mut_ptr(), buf_len as u32, &mut pos32, src.as_ptr(), frames as u32,
255 );
256 }
257 *pos = pos32 as usize;
258 } else {
259 for i in 0..frames {
260 buf[(*pos + i) % buf_len] = src[i];
261 }
262 *pos = (*pos + frames) % buf_len;
263 }
264}
265
266pub fn vec_ring_read(buf: &[f32], pos: usize, dst: &mut [f32], offset: usize) {
268 let buf_len = buf.len();
269 let frames = dst.len();
270 if use_host_ops() {
271 unsafe {
272 sesh_vec_ring_read_host(
273 buf.as_ptr(), buf_len as u32, pos as u32,
274 dst.as_mut_ptr(), offset as u32, frames as u32,
275 );
276 }
277 } else {
278 let start = (pos + buf_len - offset) % buf_len;
279 for i in 0..frames {
280 dst[i] = buf[(start + i) % buf_len];
281 }
282 }
283}
284
285pub fn vec_delay_read(buf: &[f32], pos: usize, dst: &mut [f32], time: &[f32]) {
295 let buf_len = buf.len();
296 let frames = dst.len().min(time.len());
297 if use_host_ops() {
298 unsafe {
299 sesh_vec_delay_read_host(
300 buf.as_ptr(), buf_len as u32, pos as u32,
301 dst.as_mut_ptr(), time.as_ptr(), frames as u32,
302 );
303 }
304 } else {
305 for i in 0..frames {
306 let write_pos_at_i = (pos + buf_len - frames + i) % buf_len;
308
309 let delay_int = time[i] as usize;
310 let delay_frac = time[i] - delay_int as f32;
311
312 let idx1 = (write_pos_at_i + buf_len - delay_int) % buf_len;
313 let idx2 = (idx1 + buf_len - 1) % buf_len;
314
315 dst[i] = buf[idx1] + delay_frac * (buf[idx2] - buf[idx1]);
316 }
317 }
318}
319
320pub fn vec_osc(
326 phase: &mut f32,
327 dst: &mut [f32],
328 freq: f32,
329 waveform: Waveform,
330 sample_rate: f32,
331) {
332 let frames = dst.len();
333 if use_host_ops() {
334 unsafe {
335 sesh_vec_osc_host(
336 phase as *mut f32, dst.as_mut_ptr(),
337 freq, waveform as u32, sample_rate, frames as u32,
338 );
339 }
340 } else {
341 let phase_inc = freq / sample_rate;
342 for i in 0..frames {
343 dst[i] = match waveform {
344 Waveform::Sine => (*phase * std::f32::consts::TAU).sin(),
345 Waveform::Triangle => 4.0 * (*phase - (*phase + 0.5).floor()).abs() - 1.0,
346 Waveform::Saw => 2.0 * (*phase - (*phase + 0.5).floor()),
347 Waveform::Square => if *phase % 1.0 < 0.5 { 1.0 } else { -1.0 },
348 };
349 *phase += phase_inc;
350 if *phase >= 1.0 {
351 *phase -= 1.0;
352 }
353 }
354 }
355}
356
357pub fn vec_biquad(
366 state: &mut BiquadState,
367 dst: &mut [f32],
368 src: &[f32],
369 cutoff: &[f32],
370 q: &[f32],
371 gain: &[f32],
372 filter_type: FilterType,
373 sample_rate: f32,
374) {
375 let frames = dst.len().min(src.len()).min(cutoff.len()).min(q.len()).min(gain.len());
376 if use_host_ops() {
377 unsafe {
378 sesh_vec_biquad_host(
379 state as *mut BiquadState as *mut f32,
380 dst.as_mut_ptr(), src.as_ptr(),
381 cutoff.as_ptr(), q.as_ptr(), gain.as_ptr(),
382 filter_type as u32, sample_rate, frames as u32,
383 );
384 }
385 } else {
386 for i in 0..frames {
387 let w0 = std::f32::consts::TAU * cutoff[i] / sample_rate;
388 let cos_w0 = w0.cos();
389 let sin_w0 = w0.sin();
390 let alpha = sin_w0 / (2.0 * q[i]);
391 let a_db = gain[i];
392 let a_lin = 10.0f32.powf(a_db / 40.0);
393
394 let (b0, b1, b2, a0, a1, a2) = match filter_type {
395 FilterType::Lowpass => {
396 let b1 = 1.0 - cos_w0;
397 let b0 = b1 / 2.0;
398 (b0, b1, b0, 1.0 + alpha, -2.0 * cos_w0, 1.0 - alpha)
399 }
400 FilterType::Highpass => {
401 let b1 = -(1.0 + cos_w0);
402 let b0 = (1.0 + cos_w0) / 2.0;
403 (b0, b1, b0, 1.0 + alpha, -2.0 * cos_w0, 1.0 - alpha)
404 }
405 FilterType::Bandpass => {
406 (alpha, 0.0, -alpha, 1.0 + alpha, -2.0 * cos_w0, 1.0 - alpha)
407 }
408 FilterType::Notch => {
409 (1.0, -2.0 * cos_w0, 1.0, 1.0 + alpha, -2.0 * cos_w0, 1.0 - alpha)
410 }
411 FilterType::Peak => {
412 (
413 1.0 + alpha * a_lin,
414 -2.0 * cos_w0,
415 1.0 - alpha * a_lin,
416 1.0 + alpha / a_lin,
417 -2.0 * cos_w0,
418 1.0 - alpha / a_lin,
419 )
420 }
421 FilterType::LowShelf => {
422 let two_sqrt_a_alpha = 2.0 * a_lin.sqrt() * alpha;
423 (
424 a_lin * ((a_lin + 1.0) - (a_lin - 1.0) * cos_w0 + two_sqrt_a_alpha),
425 2.0 * a_lin * ((a_lin - 1.0) - (a_lin + 1.0) * cos_w0),
426 a_lin * ((a_lin + 1.0) - (a_lin - 1.0) * cos_w0 - two_sqrt_a_alpha),
427 (a_lin + 1.0) + (a_lin - 1.0) * cos_w0 + two_sqrt_a_alpha,
428 -2.0 * ((a_lin - 1.0) + (a_lin + 1.0) * cos_w0),
429 (a_lin + 1.0) + (a_lin - 1.0) * cos_w0 - two_sqrt_a_alpha,
430 )
431 }
432 FilterType::HighShelf => {
433 let two_sqrt_a_alpha = 2.0 * a_lin.sqrt() * alpha;
434 (
435 a_lin * ((a_lin + 1.0) + (a_lin - 1.0) * cos_w0 + two_sqrt_a_alpha),
436 -2.0 * a_lin * ((a_lin - 1.0) + (a_lin + 1.0) * cos_w0),
437 a_lin * ((a_lin + 1.0) + (a_lin - 1.0) * cos_w0 - two_sqrt_a_alpha),
438 (a_lin + 1.0) - (a_lin - 1.0) * cos_w0 + two_sqrt_a_alpha,
439 2.0 * ((a_lin - 1.0) - (a_lin + 1.0) * cos_w0),
440 (a_lin + 1.0) - (a_lin - 1.0) * cos_w0 - two_sqrt_a_alpha,
441 )
442 }
443 };
444
445 let b0 = b0 / a0;
447 let b1 = b1 / a0;
448 let b2 = b2 / a0;
449 let a1 = a1 / a0;
450 let a2 = a2 / a0;
451
452 let x0 = src[i];
453 let y0 = b0 * x0 + b1 * state.x1 + b2 * state.x2
454 - a1 * state.y1 - a2 * state.y2;
455
456 state.x2 = state.x1;
457 state.x1 = x0;
458 state.y2 = state.y1;
459 state.y1 = y0;
460
461 dst[i] = y0;
462 }
463 }
464}
465
466pub fn vec_envelope(
475 state: &mut EnvelopeState,
476 dst: &mut [f32],
477 src: &[f32],
478 attack: &[f32],
479 release: &[f32],
480 mode: EnvelopeMode,
481 sample_rate: f32,
482) {
483 let frames = dst.len().min(src.len()).min(attack.len()).min(release.len());
484 if use_host_ops() {
485 unsafe {
486 sesh_vec_envelope_host(
487 state as *mut EnvelopeState as *mut f32,
488 dst.as_mut_ptr(), src.as_ptr(),
489 attack.as_ptr(), release.as_ptr(),
490 mode as u32, sample_rate, frames as u32,
491 );
492 }
493 } else {
494 for i in 0..frames {
495 let input_level = match mode {
496 EnvelopeMode::Peak => src[i].abs(),
497 EnvelopeMode::Rms => src[i] * src[i],
498 };
499
500 let att_coeff = (-1.0 / (attack[i] * sample_rate)).exp();
501 let rel_coeff = (-1.0 / (release[i] * sample_rate)).exp();
502
503 let coeff = if input_level > state.current { att_coeff } else { rel_coeff };
504 state.current = coeff * state.current + (1.0 - coeff) * input_level;
505
506 dst[i] = match mode {
507 EnvelopeMode::Peak => state.current,
508 EnvelopeMode::Rms => state.current.sqrt(),
509 };
510 }
511 }
512}
513
514pub fn vec_tanh(dst: &mut [f32], src: &[f32], drive: &[f32]) {
520 let len = dst.len().min(src.len()).min(drive.len());
521 if use_host_ops() {
522 unsafe { sesh_vec_tanh_host(dst.as_mut_ptr(), src.as_ptr(), drive.as_ptr(), len as u32) }
523 } else {
524 for i in 0..len {
525 dst[i] = (src[i] * drive[i]).tanh();
526 }
527 }
528}
529
530pub fn vec_hard_clip(dst: &mut [f32], src: &[f32], threshold: &[f32]) {
532 let len = dst.len().min(src.len()).min(threshold.len());
533 if use_host_ops() {
534 unsafe {
535 sesh_vec_hard_clip_host(dst.as_mut_ptr(), src.as_ptr(), threshold.as_ptr(), len as u32)
536 }
537 } else {
538 for i in 0..len {
539 dst[i] = src[i].clamp(-threshold[i], threshold[i]);
540 }
541 }
542}