1#[cfg(feature = "simd")]
6use std::simd::StdFloat;
7
8use bbx_core::random::XorShiftRng;
9
10#[cfg(feature = "simd")]
11use crate::polyblep::{apply_polyblamp_triangle, apply_polyblep_pulse, apply_polyblep_saw, apply_polyblep_square};
12#[cfg(feature = "simd")]
13use crate::sample::SIMD_LANES;
14use crate::{
15 polyblep::{polyblamp_triangle, polyblep_pulse, polyblep_saw, polyblep_square},
16 sample::Sample,
17};
18
19#[derive(Debug, Clone, Copy)]
21pub enum Waveform {
22 Sine,
24 Square,
26 Sawtooth,
28 Triangle,
30 Pulse,
32 Noise,
34}
35
36pub(crate) const DEFAULT_DUTY_CYCLE: f64 = 0.5;
40
41#[cfg(feature = "simd")]
45fn generate_naive_samples_simd<S: Sample>(
46 waveform: Waveform,
47 phases: S::Simd,
48 duty_cycle: S,
49 two_pi: S::Simd,
50 inv_two_pi: S::Simd,
51) -> Option<[S; SIMD_LANES]> {
52 match waveform {
53 Waveform::Sine => Some(S::simd_to_array(phases.sin())),
54
55 Waveform::Square => {
56 let half = S::simd_splat(S::from_f64(0.5));
57 let one = S::simd_splat(S::ONE);
58 let neg_one = S::simd_splat(-S::ONE);
59 let normalized = (phases % two_pi) * inv_two_pi;
60 Some(S::simd_to_array(S::simd_select_lt(normalized, half, one, neg_one)))
61 }
62
63 Waveform::Sawtooth => {
64 let two = S::simd_splat(S::from_f64(2.0));
65 let one = S::simd_splat(S::ONE);
66 let normalized = (phases % two_pi) * inv_two_pi;
67 Some(S::simd_to_array(two * normalized - one))
68 }
69
70 Waveform::Triangle => {
71 let half = S::simd_splat(S::from_f64(0.5));
72 let four = S::simd_splat(S::from_f64(4.0));
73 let one = S::simd_splat(S::ONE);
74 let three = S::simd_splat(S::from_f64(3.0));
75
76 let normalized = (phases % two_pi) * inv_two_pi;
77 let rising = four * normalized - one;
78 let falling = three - four * normalized;
79 Some(S::simd_to_array(S::simd_select_lt(normalized, half, rising, falling)))
80 }
81
82 Waveform::Pulse => {
83 let duty = S::simd_splat(duty_cycle);
84 let one = S::simd_splat(S::ONE);
85 let neg_one = S::simd_splat(-S::ONE);
86
87 let normalized = (phases % two_pi) * inv_two_pi;
88 Some(S::simd_to_array(S::simd_select_lt(normalized, duty, one, neg_one)))
89 }
90
91 Waveform::Noise => None,
92 }
93}
94
95pub(crate) fn generate_waveform_sample(
100 waveform: Waveform,
101 phase: f64,
102 phase_increment: f64,
103 duty_cycle: f64,
104 rng: &mut XorShiftRng,
105) -> f64 {
106 let normalized_phase = (phase % <f64 as Sample>::TAU) * <f64 as Sample>::INV_TAU;
107 let normalized_inc = phase_increment * <f64 as Sample>::INV_TAU;
108
109 match waveform {
110 Waveform::Sine => phase.sin(),
111 Waveform::Sawtooth => polyblep_saw(normalized_phase, normalized_inc),
112 Waveform::Square => polyblep_square(normalized_phase, normalized_inc),
113 Waveform::Pulse => polyblep_pulse(normalized_phase, normalized_inc, duty_cycle),
114 Waveform::Triangle => polyblamp_triangle(normalized_phase, normalized_inc),
115 Waveform::Noise => rng.next_noise_sample(),
116 }
117}
118
119pub(crate) fn process_waveform_scalar<S: Sample>(
124 output: &mut [S],
125 waveform: Waveform,
126 phase: &mut f64,
127 phase_increment: f64,
128 rng: &mut XorShiftRng,
129 scale: f64,
130) {
131 for sample in output.iter_mut() {
132 let value = generate_waveform_sample(waveform, *phase, phase_increment, DEFAULT_DUTY_CYCLE, rng);
133 *sample = S::from_f64(value * scale);
134 *phase += phase_increment;
135 }
136 *phase = phase.rem_euclid(<f64 as Sample>::TAU);
137}
138
139#[cfg(feature = "simd")]
143pub(crate) fn generate_waveform_samples_simd<S: Sample>(
144 waveform: Waveform,
145 phases: S::Simd,
146 phases_normalized: [S; SIMD_LANES],
147 phase_inc_normalized: S,
148 duty_cycle: S,
149 two_pi: S::Simd,
150 inv_two_pi: S::Simd,
151) -> Option<[S; SIMD_LANES]> {
152 let mut samples = generate_naive_samples_simd(waveform, phases, duty_cycle, two_pi, inv_two_pi)?;
153
154 match waveform {
155 Waveform::Sine | Waveform::Noise => {}
156 Waveform::Sawtooth => {
157 apply_polyblep_saw(&mut samples, phases_normalized, phase_inc_normalized);
158 }
159 Waveform::Square => {
160 apply_polyblep_square(&mut samples, phases_normalized, phase_inc_normalized);
161 }
162 Waveform::Pulse => {
163 apply_polyblep_pulse(&mut samples, phases_normalized, phase_inc_normalized, duty_cycle);
164 }
165 Waveform::Triangle => {
166 apply_polyblamp_triangle(&mut samples, phases_normalized, phase_inc_normalized);
167 }
168 }
169
170 Some(samples)
171}