caw_modules/
oscillator.rs

1use caw_builder_proc_macros::builder;
2use caw_core::{Buf, Sig, SigCtx, SigT};
3
4pub mod waveform {
5    use std::f32::consts::PI;
6
7    pub trait Waveform: Copy {
8        fn sample(&self, state_01: f32, pulse_width_01: f32) -> f32;
9
10        const PULSE: bool = false;
11    }
12
13    #[derive(Clone, Copy)]
14    pub struct Sine;
15    impl Waveform for Sine {
16        fn sample(&self, state_01: f32, _pulse_width_01: f32) -> f32 {
17            (state_01 * PI * 2.0).sin()
18        }
19    }
20
21    #[derive(Clone, Copy)]
22    pub struct Triangle;
23    impl Waveform for Triangle {
24        fn sample(&self, state_01: f32, _pulse_width_01: f32) -> f32 {
25            (((state_01 * 2.0) - 1.0).abs() * 2.0) - 1.0
26        }
27    }
28
29    #[derive(Clone, Copy)]
30    pub struct Saw;
31    impl Waveform for Saw {
32        fn sample(&self, state_01: f32, _pulse_width_01: f32) -> f32 {
33            (state_01 * 2.0) - 1.0
34        }
35    }
36
37    #[derive(Clone, Copy)]
38    pub struct Pulse;
39    impl Waveform for Pulse {
40        fn sample(&self, state_01: f32, pulse_width_01: f32) -> f32 {
41            if state_01 < pulse_width_01 { -1.0 } else { 1.0 }
42        }
43
44        const PULSE: bool = true;
45    }
46}
47
48use crate::{Pulse, Saw, Sine, Triangle};
49pub use waveform::Waveform;
50
51pub struct Oscillator<W, F, P, R, T>
52where
53    W: Waveform,
54    F: SigT<Item = f32>,
55    P: SigT<Item = f32>,
56    R: SigT<Item = f32>,
57    T: SigT<Item = bool>,
58{
59    first_frame: bool,
60    state_01: f32,
61    waveform: W,
62    freq_hz: F,
63    pulse_width_01: P,
64    reset_offset_01: R,
65    reset_trig: T,
66    buf: Vec<f32>,
67}
68
69impl<W, F, P, R, T> SigT for Oscillator<W, F, P, R, T>
70where
71    W: Waveform,
72    F: SigT<Item = f32>,
73    P: SigT<Item = f32>,
74    R: SigT<Item = f32>,
75    T: SigT<Item = bool>,
76{
77    type Item = f32;
78
79    fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
80        if W::PULSE {
81            self.sample_pulse(ctx);
82        } else {
83            self.sample_non_pulse(ctx);
84        }
85        &self.buf
86    }
87}
88
89impl<W, F, P, R, T> Oscillator<W, F, P, R, T>
90where
91    W: Waveform,
92    F: SigT<Item = f32>,
93    P: SigT<Item = f32>,
94    R: SigT<Item = f32>,
95    T: SigT<Item = bool>,
96{
97    fn new(
98        waveform: W,
99        freq_hz: F,
100        pulse_width_01: P,
101        reset_offset_01: R,
102        reset_trig: T,
103    ) -> Sig<Self> {
104        Sig(Self {
105            first_frame: true,
106            state_01: 0.0,
107            waveform,
108            freq_hz,
109            pulse_width_01,
110            reset_offset_01,
111            reset_trig,
112            buf: Vec::new(),
113        })
114    }
115
116    fn sample_non_pulse(&mut self, ctx: &SigCtx) {
117        let buf_freq_hz = self.freq_hz.sample(ctx);
118        let buf_reset_trig = self.reset_trig.sample(ctx);
119        let buf_reset_offset_01 = self.reset_offset_01.sample(ctx);
120        self.buf.clear();
121        for ((freq_hz, reset_trig), reset_offset_01) in buf_freq_hz
122            .iter()
123            .zip(buf_reset_trig.iter())
124            .zip(buf_reset_offset_01.iter())
125        {
126            if reset_trig || self.first_frame {
127                self.first_frame = false;
128                self.state_01 = reset_offset_01;
129            } else {
130                let state_delta = freq_hz / ctx.sample_rate_hz;
131                self.state_01 += state_delta;
132                self.state_01 = self.state_01 - (self.state_01 - 0.5).round();
133            }
134            let sample = self.waveform.sample(self.state_01, 0.0);
135            self.buf.push(sample);
136        }
137    }
138
139    // The pulse wave oscillator is specialized because in all other waveforms there's no need to
140    // compute the values of the pulse width signal.
141    fn sample_pulse(&mut self, ctx: &SigCtx) {
142        let buf_freq_hz = self.freq_hz.sample(ctx);
143        let buf_reset_trig = self.reset_trig.sample(ctx);
144        let buf_reset_offset_01 = self.reset_offset_01.sample(ctx);
145        let buf_pulse_width_01 = self.pulse_width_01.sample(ctx);
146        self.buf.clear();
147        for (((freq_hz, reset_trig), reset_offset_01), pulse_width_01) in
148            buf_freq_hz
149                .iter()
150                .zip(buf_reset_trig.iter())
151                .zip(buf_reset_offset_01.iter())
152                .zip(buf_pulse_width_01.iter())
153        {
154            if reset_trig || self.first_frame {
155                self.first_frame = false;
156                self.state_01 = reset_offset_01;
157            } else {
158                let state_delta = freq_hz / ctx.sample_rate_hz;
159                self.state_01 = (self.state_01 + state_delta).rem_euclid(1.0);
160            }
161            let sample = self.waveform.sample(self.state_01, pulse_width_01);
162            self.buf.push(sample);
163        }
164    }
165}
166
167builder! {
168    #[constructor = "oscillator"]
169    #[constructor_doc = "A signal which oscillates with a given waveform at a given freq_hzuency."]
170    #[build_fn = "Oscillator::new"]
171    #[build_ty = "Sig<Oscillator<W, F, P, R, T>>"]
172    #[generic_setter_type_name = "X"]
173    pub struct OscillatorBuilder {
174        #[generic_with_constraint = "Waveform"]
175        #[generic_name = "W"]
176        waveform: _,
177        #[generic_with_constraint = "SigT<Item = f32>"]
178        #[generic_name = "F"]
179        freq_hz: _,
180        #[generic_with_constraint = "SigT<Item = f32>"]
181        #[default = 0.5]
182        #[generic_name = "P"]
183        pulse_width_01: f32,
184        #[generic_with_constraint = "SigT<Item = f32>"]
185        #[default = 0.0]
186        #[generic_name = "R"]
187        reset_offset_01: f32,
188        #[generic_with_constraint = "SigT<Item = bool>"]
189        #[default = false]
190        #[generic_name = "T"]
191        reset_trig: bool,
192    }
193}
194
195pub struct OscillatorSaw<F, R, T>(Oscillator<Saw, F, f32, R, T>)
196where
197    F: SigT<Item = f32>,
198    R: SigT<Item = f32>,
199    T: SigT<Item = bool>;
200
201impl<F, R, T> SigT for OscillatorSaw<F, R, T>
202where
203    F: SigT<Item = f32>,
204    R: SigT<Item = f32>,
205    T: SigT<Item = bool>,
206{
207    type Item = f32;
208
209    fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
210        self.0.sample_non_pulse(ctx);
211        &self.0.buf
212    }
213}
214
215impl<F, R, T> OscillatorSaw<F, R, T>
216where
217    F: SigT<Item = f32>,
218    R: SigT<Item = f32>,
219    T: SigT<Item = bool>,
220{
221    fn new(freq_hz: F, reset_offset_01: R, reset_trig: T) -> Sig<Self> {
222        Sig(Self(
223            Oscillator::new(Saw, freq_hz, 0., reset_offset_01, reset_trig).0,
224        ))
225    }
226}
227
228builder! {
229    #[constructor = "saw"]
230    #[constructor_doc = "A signal which oscillates with a saw wave at a given freq_hzuency."]
231    #[build_fn = "OscillatorSaw::new"]
232    #[build_ty = "Sig<OscillatorSaw<F, R, T>>"]
233    #[generic_setter_type_name = "X"]
234    pub struct OscillatorSawBuilder {
235        #[generic_with_constraint = "SigT<Item = f32>"]
236        #[generic_name = "F"]
237        freq_hz: _,
238        #[generic_with_constraint = "SigT<Item = f32>"]
239        #[default = 0.0]
240        #[generic_name = "R"]
241        reset_offset_01: f32,
242        #[generic_with_constraint = "SigT<Item = bool>"]
243        #[default = false]
244        #[generic_name = "T"]
245        reset_trig: bool,
246    }
247}
248
249pub struct OscillatorSine<F, R, T>(Oscillator<Sine, F, f32, R, T>)
250where
251    F: SigT<Item = f32>,
252    R: SigT<Item = f32>,
253    T: SigT<Item = bool>;
254
255impl<F, R, T> SigT for OscillatorSine<F, R, T>
256where
257    F: SigT<Item = f32>,
258    R: SigT<Item = f32>,
259    T: SigT<Item = bool>,
260{
261    type Item = f32;
262
263    fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
264        self.0.sample_non_pulse(ctx);
265        &self.0.buf
266    }
267}
268
269impl<F, R, T> OscillatorSine<F, R, T>
270where
271    F: SigT<Item = f32>,
272    R: SigT<Item = f32>,
273    T: SigT<Item = bool>,
274{
275    fn new(freq_hz: F, reset_offset_01: R, reset_trig: T) -> Sig<Self> {
276        Sig(Self(
277            Oscillator::new(Sine, freq_hz, 0., reset_offset_01, reset_trig).0,
278        ))
279    }
280}
281
282builder! {
283    #[constructor = "sine"]
284    #[constructor_doc = "A signal which oscillates with a sine wave at a given freq_hzuency."]
285    #[build_fn = "OscillatorSine::new"]
286    #[build_ty = "Sig<OscillatorSine<F, R, T>>"]
287    #[generic_setter_type_name = "X"]
288    pub struct OscillatorSineBuilder {
289        #[generic_with_constraint = "SigT<Item = f32>"]
290        #[generic_name = "F"]
291        freq_hz: _,
292        #[generic_with_constraint = "SigT<Item = f32>"]
293        #[default = 0.0]
294        #[generic_name = "R"]
295        reset_offset_01: f32,
296        #[generic_with_constraint = "SigT<Item = bool>"]
297        #[default = false]
298        #[generic_name = "T"]
299        reset_trig: bool,
300    }
301}
302
303pub struct OscillatorTriangle<F, R, T>(Oscillator<Triangle, F, f32, R, T>)
304where
305    F: SigT<Item = f32>,
306    R: SigT<Item = f32>,
307    T: SigT<Item = bool>;
308
309impl<F, R, T> SigT for OscillatorTriangle<F, R, T>
310where
311    F: SigT<Item = f32>,
312    R: SigT<Item = f32>,
313    T: SigT<Item = bool>,
314{
315    type Item = f32;
316
317    fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
318        self.0.sample_non_pulse(ctx);
319        &self.0.buf
320    }
321}
322
323impl<F, R, T> OscillatorTriangle<F, R, T>
324where
325    F: SigT<Item = f32>,
326    R: SigT<Item = f32>,
327    T: SigT<Item = bool>,
328{
329    fn new(freq_hz: F, reset_offset_01: R, reset_trig: T) -> Sig<Self> {
330        Sig(Self(
331            Oscillator::new(Triangle, freq_hz, 0., reset_offset_01, reset_trig)
332                .0,
333        ))
334    }
335}
336
337builder! {
338    #[constructor = "triangle"]
339    #[constructor_doc = "A signal which oscillates with a triangle wave at a given freq_hzuency."]
340    #[build_fn = "OscillatorTriangle::new"]
341    #[build_ty = "Sig<OscillatorTriangle<F, R, T>>"]
342    #[generic_setter_type_name = "X"]
343    pub struct OscillatorTriangleBuilder {
344        #[generic_with_constraint = "SigT<Item = f32>"]
345        #[generic_name = "F"]
346        freq_hz: _,
347        #[generic_with_constraint = "SigT<Item = f32>"]
348        #[default = 0.0]
349        #[generic_name = "R"]
350        reset_offset_01: f32,
351        #[generic_with_constraint = "SigT<Item = bool>"]
352        #[default = false]
353        #[generic_name = "T"]
354        reset_trig: bool,
355    }
356}
357
358pub struct OscillatorPulse<F, P, R, T>(Oscillator<Pulse, F, P, R, T>)
359where
360    F: SigT<Item = f32>,
361    P: SigT<Item = f32>,
362    R: SigT<Item = f32>,
363    T: SigT<Item = bool>;
364
365impl<F, P, R, T> SigT for OscillatorPulse<F, P, R, T>
366where
367    F: SigT<Item = f32>,
368    P: SigT<Item = f32>,
369    R: SigT<Item = f32>,
370    T: SigT<Item = bool>,
371{
372    type Item = f32;
373
374    fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
375        self.0.sample_pulse(ctx);
376        &self.0.buf
377    }
378}
379
380impl<F, P, R, T> OscillatorPulse<F, P, R, T>
381where
382    F: SigT<Item = f32>,
383    P: SigT<Item = f32>,
384    R: SigT<Item = f32>,
385    T: SigT<Item = bool>,
386{
387    fn new(
388        freq_hz: F,
389        pulse_width_01: P,
390        reset_offset_01: R,
391        reset_trig: T,
392    ) -> Sig<Self> {
393        Sig(Self(
394            Oscillator::new(
395                Pulse,
396                freq_hz,
397                pulse_width_01,
398                reset_offset_01,
399                reset_trig,
400            )
401            .0,
402        ))
403    }
404}
405
406builder! {
407    #[constructor = "pulse"]
408    #[constructor_doc = "A signal which oscillates with a pulse wave at a given freq_hzuency."]
409    #[build_fn = "OscillatorPulse::new"]
410    #[build_ty = "Sig<OscillatorPulse<F, P, R, T>>"]
411    #[generic_setter_type_name = "X"]
412    pub struct OscillatorPulseBuilder {
413        #[generic_with_constraint = "SigT<Item = f32>"]
414        #[generic_name = "F"]
415        freq_hz: _,
416        #[generic_with_constraint = "SigT<Item = f32>"]
417        #[default = 0.5]
418        #[generic_name = "P"]
419        pulse_width_01: f32,
420        #[generic_with_constraint = "SigT<Item = f32>"]
421        #[default = 0.0]
422        #[generic_name = "R"]
423        reset_offset_01: f32,
424        #[generic_with_constraint = "SigT<Item = bool>"]
425        #[default = false]
426        #[generic_name = "T"]
427        reset_trig: bool,
428    }
429}