rill_core_dsp/generators/
wavetable.rs1use crate::algorithm::{Algorithm, AlgorithmCategory, AlgorithmMetadata};
2use crate::generators::{Generator, InterpolatedReader};
3use crate::vector::prelude::*;
4use rill_core::traits::{ActionContext, ProcessResult};
5use rill_core::Transcendental;
6
7pub struct WavetableOscillator<T: Transcendental, const SIZE: usize> {
13 reader: InterpolatedReader<T>,
14 frequency: f32,
15 amplitude: ScalarVector1<T>,
16 sample_rate: f32,
17}
18
19impl<T: Transcendental, const SIZE: usize> WavetableOscillator<T, SIZE> {
20 pub fn new(table: [T; SIZE], frequency: f32) -> Self {
22 let mut reader = InterpolatedReader::new(table.to_vec());
23 reader.set_wrap(true);
24 let mut osc = Self {
25 reader,
26 frequency,
27 amplitude: ScalarVector1::splat(T::from_f32(1.0)),
28 sample_rate: 44100.0,
29 };
30 osc.update_rate();
31 osc
32 }
33
34 pub fn sine(frequency: f32) -> Self {
36 let mut table = [T::ZERO; SIZE];
37 for i in 0..SIZE {
38 let phase = (i as f32 / SIZE as f32) * 2.0 * core::f32::consts::PI;
39 table[i] = T::from_f32(phase.sin());
40 }
41 Self::new(table, frequency)
42 }
43
44 pub fn saw(frequency: f32) -> Self {
46 let mut table = [T::ZERO; SIZE];
47 for i in 0..SIZE {
48 table[i] = T::from_f32(2.0 * i as f32 / SIZE as f32 - 1.0);
49 }
50 Self::new(table, frequency)
51 }
52
53 pub fn set_table(&mut self, table: [T; SIZE]) {
55 self.reader.set_buffer(table.to_vec());
56 }
57
58 pub fn set_cubic(&mut self, cubic: bool) {
60 self.reader.set_cubic(cubic);
61 }
62
63 pub fn is_cubic(&self) -> bool {
65 self.reader.is_cubic()
66 }
67
68 fn update_rate(&mut self) {
69 let rate = self.frequency as f64 * SIZE as f64 / self.sample_rate as f64;
70 self.reader.set_rate(rate);
71 }
72}
73
74impl<T: Transcendental, const SIZE: usize> Algorithm<T> for WavetableOscillator<T, SIZE> {
75 fn init(&mut self, sample_rate: f32) {
76 self.sample_rate = sample_rate;
77 self.update_rate();
78 self.reader.set_position(0.0);
79 }
80
81 fn reset(&mut self) {
82 self.reader.set_position(0.0);
83 }
84
85 fn process(
86 &mut self,
87 _input: Option<&[T]>,
88 output: &mut [T],
89 _ctx: &ActionContext,
90 ) -> ProcessResult<()> {
91 let amp = self.amplitude.extract(0);
92 self.reader.render_block(output);
93 if amp != T::from_f32(1.0) {
94 for s in output.iter_mut() {
95 *s = *s * amp;
96 }
97 }
98 Ok(())
99 }
100
101 fn metadata(&self) -> AlgorithmMetadata {
102 AlgorithmMetadata {
103 name: "Wavetable Oscillator",
104 category: AlgorithmCategory::Generator,
105 description: "Wavetable oscillator with linear / cubic interpolation".into(),
106 author: "Rill",
107 version: env!("CARGO_PKG_VERSION"),
108 }
109 }
110}
111
112impl<T: Transcendental, const SIZE: usize> Generator<T> for WavetableOscillator<T, SIZE> {
113 fn phase(&self) -> T {
114 let pos = self.reader.position();
115 let len = SIZE as f64;
116 T::from_f64((pos % len) / len)
117 }
118
119 fn set_phase(&mut self, phase: T) {
120 let p = phase.to_f64().clamp(0.0, 1.0);
121 self.reader.set_position(p * SIZE as f64);
122 }
123
124 fn reset_phase(&mut self) {
125 self.reader.set_position(0.0);
126 }
127
128 fn frequency(&self) -> f32 {
129 self.frequency
130 }
131
132 fn set_frequency(&mut self, freq: f32) {
133 self.frequency = freq;
134 self.update_rate();
135 }
136
137 fn amplitude(&self) -> T {
138 self.amplitude.extract(0)
139 }
140
141 fn set_amplitude(&mut self, amp: T) {
142 self.amplitude = ScalarVector1::splat(amp.clamp(T::ZERO, T::from_f32(1.0)));
143 }
144}